/* planarity.c - code for planarity testing of undirected graphs.
 * Method of Boyer and Myrvold, programmed by Paulette Lieby.
 * The copyright of this program is owned by the Magma project.
 * Distributed with nauty by permission.
 ***************************************************************/
 
/*
 *  sparseg_adjl.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Some high-level functions on the sparse graph as
  an adjacency list.
  In particular, testing if it is planar.
  


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

#include "planarity.h"

#define IF_DEB(x)    {}
#define IF_VERB(x)   {}



/* aproto: header embed_graph_protos.h */


#ifndef PLANAR_IN_MAGMA
#endif


boolean 
sparseg_adjl_plan_and_iso (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
        int e, int *c, t_ver_sparse_rep **VR, t_adjl_sparse_rep **AR,
        t_embed_sparse_rep **ER, int *nbr_e_obs)
    /*
      the input graph is given as an adjacency list:
      V: array of vertices 
      n: size of graph
      A: adjacency list
      e: number of edges
      
      if the graph is planar the embedding is stored  in VR and ER;
      the embedding contains e edges
      (nbr_e_obs not used)
      
      if the graph is non planar the obstruction is returned in
      VR and AR together with the number of edges in nbr_e_obs

      in all cases is also returned the number of components (in c)
    */
{
    t_dlcl           **dfs_tree, **back_edges, **mult_edges;
    int              edge_pos, v, w;
    boolean          ans;
    t_ver_edge       *embed_graph;
 
    ans = sparseg_adjl_is_planar(V, n, A, c,
                                 &dfs_tree, &back_edges, &mult_edges,
                                 &embed_graph, &edge_pos, &v, &w);
    
    if (!ans)
    {
        embedg_obstruction(V, A, dfs_tree, back_edges,
                           embed_graph, n, &edge_pos,
                           v, w, VR, AR, nbr_e_obs);
    }
    else
    {
        embedg_embedding(V, A, embed_graph, n, e, *c, edge_pos, mult_edges,
                         VR, ER);
    }
    
    sparseg_dlcl_delete(dfs_tree, n);
    sparseg_dlcl_delete(back_edges, n);
    sparseg_dlcl_delete(mult_edges, n);
    embedg_VES_delete(embed_graph, n);
 
    return ans;
}



int *
sparseg_adjl_footprint (t_ver_sparse_rep *V, int n,
        t_adjl_sparse_rep *A, int v)
    /*
      return v's footprint:
      an array fp of size n where fp[i] = index of (directed)
      edge [v, i] in A
    */
{
    /*
      note that we won't initialise the array:
      its subsequent usage doesn't require it
    */
    int        *fp, e;

    fp = (int *) mem_malloc(sizeof(int) * n);

    if (V[v].first_edge == NIL)
        /*
          do nothing
        */
        return fp;

    e = V[v].first_edge;
    while (e != NIL)
    {
        fp[A[e].end_vertex] = e;
        e = A[e].next;
    }

    return fp;
}


void 
sparseg_adjl_print (t_ver_sparse_rep *V, int n,
        t_adjl_sparse_rep *A, boolean user_level)
{
    int        v;

    for (v = 0; v < n; v++)
    {
        int     next;

        if (user_level)
            fprintf(stdout, "%d:\t", v + 1);
        else
            fprintf(stdout, "%d:\t", v);
        
        next = V[v].first_edge;
        while (next != NIL)
        {
            if (user_level)
                fprintf(stdout, "%d ", A[next].end_vertex + 1);
            else
                fprintf(stdout, "%d ", A[next].end_vertex);
            
            next = A[next].next;
        }
        fprintf(stdout, "\n");
    }
}




void 
sparseg_adjl_embed_print (t_ver_sparse_rep *V_e, int n,
        t_adjl_sparse_rep *A, t_embed_sparse_rep *E, boolean user_level)
    /*
      print the embedding given by E,
      edges are referred to by their index in A

      and V_e[v].first_edge is the index in E of the first edge
      (in the embedding's order) incident from v

      note that E is NOT indexed by the same vertices' array
      that indexes A (at the creation of the sparse graph)
    */
{
    int        v;

    for (v = 0; v < n; v++)
    {
        int      start, next;

        if (user_level)
            fprintf(stdout, "%d:\t", v + 1);
        else
            fprintf(stdout, "%d:\t", v);

        if (V_e[v].first_edge == NIL)
        {
            fprintf(stdout, "\n");
            continue;
        }
        start = next = V_e[v].first_edge;

        if (user_level)
            fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex + 1);
        else
            fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex);

        next = E[next].next;
        
        while (next != start)
            /*
              recall that in E edges are linked into a circular list
            */
        {
            if (user_level)
                fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex + 1);
            else
                fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex);

            next = E[next].next;
        }
        fprintf(stdout, "\n");
    }
}

graph *
sparseg_adjl_to_nauty_graph (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A)
    /*
      write the sparse graph as a nauty graph
    */
{
    int          m, v, e, i;
    graph        *g;
 
    m = (n + WORDSIZE - 1) / WORDSIZE;
    g = (graph *) mem_malloc(n * m * sizeof(graph));
    for (i = (long) m * n; --i >= 0;)
        g[i] = 0;
 
    /*
      we first copy V and A's information into g
    */
    for (v = 0; v < n; v++)
    {
        e = V[v].first_edge;
        while (e != NIL)
            /*
              A[e].end_vertex is the next neighbour in the list,
              A[e].next points to the next edge in the list
            */
        {
            if (A[e].end_vertex != v)  /* no loops */
            {
                ADDELEMENT(GRAPHROW(g, v, m), A[e].end_vertex);
            }
            e = A[e].next;
        }
    }

    return g;
}



#if 0
t_edge_sparse_rep *
sparseg_adjl_edges (t_ver_sparse_rep *V, int n,
        t_adjl_sparse_rep *A, int e, boolean digraph)
    /*
      e is the number of edges
    */
{
    t_edge_sparse_rep *edges;
    int               m, u, v, pos_e;
    graph             *g;

    edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);

    m = (n + WORDSIZE - 1) / WORDSIZE;
    g = sparseg_adjl_to_nauty_graph(V, n, A);

    pos_e = 0;
    for (u = 0; u < n; u++)
    {
        v = digraph == TRUE ? 0 : u + 1;
        for (; v < n; v++)
        {
            if (ISELEMENT(GRAPHROW(g, u, m), v))
            {
                t_edge_sparse_rep edge;

                edge.ends[0] = u;
                edge.ends[1] = v;
                edges[pos_e++] = edge;
            }
        }
    }
    ASSERT(pos_e == e);
    mem_free(g);
    
    return edges;
}
#endif



t_edge_sparse_rep *
sparseg_adjl_edges (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
        int e, boolean digraph)
    /*
      e is the number of edges
    */
{
#if 0
    t_edge_sparse_rep   *edges;
    int                 u, v, pos_e, *loops, *foot_print;
    graph               *g;
 
    loops = (int *) mem_malloc(sizeof(int) * n);
    for (v = 0; v < n; v++)
    {
        loops[v] = 0;
    }
 
    edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
    pos_e = 0;

    foot_print = (int *) mem_malloc(sizeof(int) * n);
    for (u = 0; u < n; u++)
        foot_print[u] = NIL;
    
    for (v = 0; v < n; v++)
    {
        int               ne;
        t_edge_sparse_rep edge;

        ne = V[v].first_edge;
        while (ne != NIL)
        {
            u = A[ne].end_vertex;
            if (digraph
                || (!digraph && u > v))
            {
                foot_print[u] = v;
            }
            else if (!digraph && u == v)
            {
                if (loops[v] == 0)
                {
                    foot_print[u] = v;
                }
 
                loops[v] ^= 1;
            }

            ne = A[ne].next;
        }

        for (u = 0; u < n; u++)
            if (foot_print[u] == v)
            {
                edge.ends[0] = v;
                edge.ends[1] = u;
                edges[pos_e++] = edge;
            }
    }
    ASSERT(pos_e == e);
    mem_free(loops);
    mem_free(foot_print);
    
    return edges;
    
#endif   
    /*
      there must be a simpler way
    */
#if 0
    typedef struct edge_list {
        int               size;
        t_edge_sparse_rep *edges;
    } t_edge_list;
 
    t_edge_list         *edge_table;        
    t_edge_sparse_rep   *edges;
    int                 u, v, nbr_e, pos_e, *loops;
    graph               *g;

    loops = (int *) mem_malloc(sizeof(int) * n);
    for (v = 0; v < n; v++)
    {
        loops[v] = 0;
    }

    /*
      now create an edge table as follows:
      - there are n lists in total
      - their respective size is given by size
      - their contents by *edges:

      edge_table[i] will contain all the edges whose end-point is i:
      these edges, by construction, will be sorted according to their
      starting point

      what for? to finish off each start-vertex processing
      with a bucket sort so that
      the edges are sorted wrt start- & end-point

      bucket sort is linear, hence why...
    */
    edge_table = (t_edge_list *) mem_malloc(sizeof(t_edge_list) * n);
    for (v = 0; v < n; v++)
    {
        edge_table[v].size = 0;
        edge_table[v].edges = NP;
    }

    edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
    
    nbr_e = 0;
    pos_e = 0;
    for (v = 0; v < n; v++)
    {
        int    ne, w, u;

        ne = V[v].first_edge;
        while (ne != NIL)
        {
            u = A[ne].end_vertex;
            if (digraph
                || (!digraph && u > v))
            {
                t_edge_sparse_rep edge;
 
                edge.ends[0] = v;
                edge.ends[1] = u;

                /*
                  now stick this edge into the table: one may ponder
                  as to the cost of constantly reallocating memory...
                  some cursory tests in another context tell me that
                  this is pretty much ok
                  (and certainly better than allocating n^2 storage space)
                */
                if (edge_table[u].size == 0)
                {
                    edge_table[u].edges = (t_edge_sparse_rep *)
                        mem_malloc(sizeof(t_edge_sparse_rep));
                }
                else
                {
                    edge_table[u].edges = (t_edge_sparse_rep *)
                         mem_realloc(edge_table[u].edges,
                                     sizeof(t_edge_sparse_rep)
                                     * (edge_table[u].size + 1));
                }

                (edge_table[u].edges)[edge_table[u].size] = edge;
                edge_table[u].size += 1;
                nbr_e++;
            }
            else if (!digraph && u == v)
            {
                if (loops[v] == 0)
                {
                    t_edge_sparse_rep edge;
 
                    edge.ends[0] = v;
                    edge.ends[1] = u;

                    if (edge_table[u].size == 0)
                    {
                        edge_table[u].edges = (t_edge_sparse_rep *)
                            mem_malloc(sizeof(t_edge_sparse_rep));
                    }
                    else
                    {
                        edge_table[u].edges = (t_edge_sparse_rep *)
                            mem_realloc(edge_table[u].edges,
                                        sizeof(t_edge_sparse_rep)
                                        * (edge_table[u].size + 1));
                    }
                    
                    (edge_table[u].edges)[edge_table[u].size] = edge;
                    edge_table[u].size += 1;
                    nbr_e++;
                }
                
                loops[v] ^= 1;
            }
            
            ne = A[ne].next;
        }

        /*
          bucket sort must take place here:
          of course the whole lot is not exactly linear!
          since we perform the sort n times; but we can hope for
          a "good"  ?? average behaviour:

          in any case this must be better that checking adjacencies
          n^2 times in a sparse rep. (see edge_set_iset_assure)
        */
        for (w = 0; w < n; w++)
        {
            if (edge_table[w].size > 0)
            {
                for (u = 0; u < edge_table[w].size; u++)
                {
                    ASSERT((edge_table[w].edges)[u].ends[0] == v);
                    edges[pos_e++] = (edge_table[w].edges)[u];
                }
                mem_free(edge_table[w].edges);
                edge_table[w].size = 0;
                edge_table[w].edges = NP;
            }
        }
    }
    ASSERT(nbr_e == e);
    ASSERT(pos_e == e);
    mem_free(loops);
    mem_free(edge_table);
    
    return edges;
#endif

    t_edge_sparse_rep *edges;
    int               v, pos_e, *loops;

    edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
    loops = (int *) mem_malloc(sizeof(int) * n);
    for (v = 0; v < n; v++)
    {
        loops[v] = 0;
    }
    
    pos_e = 0;
    for (v = 0; v < n; v++)
    {
        int    ne;

        ne = V[v].first_edge;
        while (ne != NIL)
        {
            int      u;

            u = A[ne].end_vertex;
            if (digraph
                || (!digraph && u > v))
            {
                t_edge_sparse_rep edge;
 
                edge.ends[0] = v;
                edge.ends[1] = u;
                edges[pos_e++] = edge;
            }
            else if (!digraph && u == v)
            {
                if (loops[v] == 0)
                {
                    t_edge_sparse_rep edge;
 
                    edge.ends[0] = v;
                    edge.ends[1] = u;
                    edges[pos_e++] = edge;
                }

                loops[v] ^= 1;
            }
            ne = A[ne].next;
        }
    }
    ASSERT(pos_e == e);
    mem_free(loops);
    
    return edges;

}

/*
 *  sparseg_adjl_modify.c
 */
 
/*
  What:
  *****
  
  Implementing:
 
  Some high-level functions on the sparse graph as
  an adjacency list.
  In particular, adding/removing vertices/edges.


  NOTE: Most of the functions implicitely assume that the
  graph is undirected;
  this must be slightly rewritten for the general case
  -- just haven't got the time right now...


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

#include "planarity.h"

#define IF_DEB(x)    {}
#define IF_VERB(x)   {}


/* aproto: header embed_graph_protos.h */



#ifndef PLANAR_IN_MAGMA
#endif



boolean 
sparseg_adjl_add_edge (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep **A,
        int *size_A, int *pos, int u, int v, boolean CHECK)
    /*
      add the UNDIRECTED edge to the sparse graph (V, n, A)
      - pos records where to add the next edge in A
      - if pos + 1 == size_A, we must extend A

      we check if the edge is already in the graph iff CHECK true

      also we assume that the graph (V, n, A) is undirected
    */
{
    boolean             edge_exists;

    edge_exists = FALSE;
    if (CHECK)
    {
        edge_exists = sparseg_adjl_dir_edge_exists(V, n, *A, u, v);

        if (edge_exists)
            return FALSE;
    }
    
    if (*pos == *size_A)
    {
        IF_DEB(
               fprintf(stdout, "realloc \n");
               )

        *size_A += 2;    /* add two directed edges */
        *A = (t_adjl_sparse_rep *)
            mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
    }
    else if (*pos + 1 == *size_A)
    {
        IF_DEB(
               fprintf(stdout, "realloc \n");
               )

        *size_A += 1;    /* add two directed edges */
        *A = (t_adjl_sparse_rep *)
            mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
    }
    ASSERT(*pos + 1 < *size_A);
    
    sparseg_adjl_add_dir_edge(V, n, A, size_A, pos, u, v, FALSE);
    sparseg_adjl_add_dir_edge(V, n, A, size_A, pos, v, u, FALSE);

    return TRUE;
}
 
boolean 
sparseg_adjl_add_edge_no_extend (t_ver_sparse_rep *V, int n,
    t_adjl_sparse_rep *A, int size_A, int *pos, int u, int v, boolean CHECK)
    /*
      like sparseg_adjl_add_edge but here we are guaranteed
      that pos + 1 < size_A
      (unless that for some reason we attempt to add
      an edge which is already there)
      
      this feature is required when A is part of a Magma block:
      we do not want to reallocate A here
      (would be done at a higher level)
 
      we check if the edge is already in the graph iff CHECK true

      also, we assume that we use this procedur only when dealing
      with an undirected graph
    */
{
    boolean   edge_added;
   
    edge_added =
        sparseg_adjl_add_dir_edge_no_extend(V, n, A, size_A, pos, u, v,
                                            CHECK);

    if (edge_added)
        sparseg_adjl_add_dir_edge_no_extend(V, n, A, size_A, pos, v, u,
                                            FALSE);

    return edge_added;
}


boolean 
sparseg_adjl_add_dir_edge (t_ver_sparse_rep *V, int n,
        t_adjl_sparse_rep **A, int *size_A, int *pos, int u, int v,
        boolean CHECK)
    /*
      add the DIRECTED edge to the sparse graph (V, n, A)
      - pos records where to add the next edge in A
      - if pos >= size_A, we must extend A
 
      we check if the edge is already in the graph iff CHECK true
    */
{
    boolean             edge_exists;

    edge_exists = FALSE;
    if (CHECK)
    {
        edge_exists = sparseg_adjl_dir_edge_exists(V, n, *A, u, v);
 
        if (edge_exists)
            return FALSE;
    }

    if (*pos == *size_A)
    {
        *size_A += 1;    /* add one directed edge */
        *A = (t_adjl_sparse_rep *)
            mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
    }
    ASSERT(*pos < *size_A);

    sparseg_adjl_add_dir_edge_no_extend(V, n, *A, *size_A, pos, u, v,
                                        FALSE);

    return TRUE;
}
 
boolean 
sparseg_adjl_add_dir_edge_no_extend (t_ver_sparse_rep *V, int n,
     t_adjl_sparse_rep *A, int size_A, int *pos, int u, int v, boolean CHECK)
    /*
      add an edge where A is guaranteed to be be big enough
      (unless that for some reason we attempt to add
      an edge which is already there)

      this feature is required when A is part of a Magma block:
      we do not want to reallocate A here
      (would be done at a higher level)
 
      we check if the edge is already in the graph iff CHECK true
    */
{
    /*
      given the way V and A represent the graph, it is simplest
      to add the new edge at the beginning of i's adj. list
    */
    int                  i_v;
    t_adjl_sparse_rep    a;

    if (CHECK && sparseg_adjl_dir_edge_exists(V, n, A, u, v))
        return FALSE;
    
    if (*pos >= size_A)
        DIE();

    /*
      otherwise always add the edge
    */
    i_v = *pos;
    a.end_vertex = v;
    a.next = V[u].first_edge;
    A[(*pos)++] = a;
    V[u].first_edge = i_v;

    return TRUE;
}
 


boolean 
sparseg_adjl_remove_edge_no_red (t_ver_sparse_rep *V, t_adjl_sparse_rep *A,
        int u, int v)
    /*
      remove the UNDIRECTED edge from sparse graph (V, A)
      if (u, v) is not an edge then nothing changes (and return FALSE)

      A will be left with "holes"
    */
{
    sparseg_adjl_remove_dir_edge_no_red(V, A, u, v);
    return sparseg_adjl_remove_dir_edge_no_red(V, A, v, u);
}
 

boolean 
sparseg_adjl_remove_dir_edge_no_red (t_ver_sparse_rep *V,
        t_adjl_sparse_rep *A, int u, int v)
    /*
      remove the DIRECTED edge from the sparse graph (V, n, A)
      if (u, v) is not an edge then nothing changes  (and return FALSE)

      A will be left with "holes"
    */
{
    int         cur_e, prev_e;

    cur_e = V[u].first_edge;
    if (cur_e == NIL)
        /*
          (u, v) is not an edge
        */
        return FALSE;

    if (A[cur_e].end_vertex == v)
    {
        V[u].first_edge = A[cur_e].next;
        return TRUE;   /* done */
    }

    while (A[cur_e].end_vertex != v)
        /*
          if (u, v) is an edge then this loop will terminate
        */
    {
        prev_e = cur_e;
        cur_e = A[cur_e].next;
        if (cur_e == NIL)
            /*
              (u, v) is not an edge
            */
            return FALSE;
    }
    ASSERT(A[cur_e].end_vertex == v);

    A[prev_e].next = A[cur_e].next;
    return TRUE;
}
 
int 
sparseg_adjl_remove_all_dir_edge_no_red (t_ver_sparse_rep *V,
        t_adjl_sparse_rep *A, int u, int v)
    /*
      remove all DIRECTED edges [u, v] from the non-simple
      sparse graph (V, n, A)
      if (u, v) is not an edge then nothing changes;
      we return the number of edges removed

      A will be left with "holes"
    */
{
    int         cur_e, prev_e, e_removed;

    if (V[u].first_edge == NIL)
        /*
          (u, v) is not an edge
        */
        return 0;

    e_removed = 0;
    while (A[V[u].first_edge].end_vertex == v)
    {
        V[u].first_edge = A[V[u].first_edge].next;
        e_removed++;

        if (V[u].first_edge == NIL)
            return e_removed;
    }
    ASSERT(A[V[u].first_edge].end_vertex != v);
    
    prev_e = V[u].first_edge;
    cur_e = A[prev_e].next;
    while (cur_e != NIL)
    {
        if (A[cur_e].end_vertex == v)
        {
            A[prev_e].next = A[cur_e].next;
            e_removed++;
            cur_e = A[cur_e].next;
        }
        else
        {
            prev_e = cur_e;
            cur_e = A[cur_e].next;
        }
    }

    return e_removed;
}
 


void 
sparseg_adjl_add_vertices (t_ver_sparse_rep **V, int n, int nmore)
    /*
      add nmore vertices
      V is assumed to have length n 
    */
{
    *V = (t_ver_sparse_rep *)
        mem_realloc(*V, sizeof(t_ver_sparse_rep) * (n + nmore));

    sparseg_adjl_add_vertices_no_extend(*V, n, nmore);
}
 
void 
sparseg_adjl_add_vertices_no_extend (t_ver_sparse_rep *V, int n, int nmore)
    /*
      add nmore vertices,
      here V is assumed to have length n + nmore (ie V has already
      been made bigger)
    */
{
    int                  v;

    for (v = n; v < n + nmore; v++)
    {
        V[v].first_edge = NIL;
    }
}
 
void 
sparseg_adjl_remove_vertex (t_ver_sparse_rep **V, int n,
        t_adjl_sparse_rep *A, int pos_A, int w, int *e)
    /*
      V is assumed to have length n: we will reallocate
      V so that V will have length n-1

      A is occupied from [0..pos-1], A will be left with holes

      we also assume that the graph can have loops and multiple edges;
      further, we the edge counting implicitely assumes that graph
      is undirected!!!

      this must be eventually fixed
    */
{
    int                  v, nv, edge, loops;
    t_ver_sparse_rep     *new_V;

    /*
      we first count the loops if any 
    */
    loops = 0;
    edge = (*V)[w].first_edge;
    while (edge != NIL)
    {
        loops = A[edge].end_vertex == w ? loops + 1 : loops;
        edge = A[edge].next;
    }
    ASSERT(loops % 2 == 0);
    loops /= 2;

    /*
      we recreate the vertices array
    */
    new_V = (t_ver_sparse_rep *)
        mem_malloc(sizeof(t_ver_sparse_rep) * (n - 1));

    for (v = 0, nv = 0; v < n; v++, nv++)
    {
        if (v == w)
        {
            nv--;
        }
        else
        {
            new_V[nv].first_edge = (*V)[v].first_edge;
        }
    }
    mem_free(*V);
    *V = new_V;

    *e -= loops;
    sparseg_adjl_remove_vertex_no_red(*V, n, A, w, e);
    
    /*
      oops! not relabelling vertices can wreck havock!
    */
    sparseg_adjl_relabel_vertex(A, pos_A, w);
}
 
void 
sparseg_adjl_remove_vertex_no_red (t_ver_sparse_rep *V, int n,
        t_adjl_sparse_rep *A, int w, int *e)
    /*
      here V has already size n - 1 and has been initialised,
      all what remains to do is to remove the edges incident
      from w in A

      A will be left with holes
    */
{
    int                  v, nbr_e_removed;

    nbr_e_removed = 0;
    for (v = 0; v < n - 1; v++)
    {
        nbr_e_removed += sparseg_adjl_remove_all_dir_edge_no_red(V, A, v, w);
    }

    *e= *e - nbr_e_removed;
}
 
void 
sparseg_adjl_relabel_vertex (t_adjl_sparse_rep *A, int pos, int u)
    /*
      relabel all vertices v > u as v-1
      (required when removing a vertex)
    */
{
    int                  i;

    for (i = 0; i < pos; i++)
    {
        A[i].end_vertex = A[i].end_vertex > u ?
            A[i].end_vertex - 1 : A[i].end_vertex;
    }
}
 
/*
 *  sparseg_adjl_pred.c
 */
 
/*
  What:
  *****
  
  Implementing:
 
  Some high-level functions on the sparse graph as
  an adjacency list: predicates.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

#include "planarity.h"

#define IF_DEB(x)    {}
#define IF_VERB(x)   {}



/* aproto: header embed_graph_protos.h */


#ifndef PLANAR_IN_MAGMA
#endif

boolean 
sparseg_adjl_dir_edge_exists (t_ver_sparse_rep *V, int n,
        t_adjl_sparse_rep *A, int u, int v)
    /*
      does the directed edge [u, v] already exist in the graph
    */
{
    int         cur_e, prev_e;

    cur_e = V[u].first_edge;
    if (cur_e == NIL)
        return FALSE;

    if (A[cur_e].end_vertex == v)
    {
        return TRUE; 
    }

    while (A[cur_e].end_vertex != v)
    {
        prev_e = cur_e;
        cur_e = A[cur_e].next;
        if (cur_e == NIL)
            /*
              (u, v) is not an edge
            */
            return FALSE;
    }
    ASSERT(A[cur_e].end_vertex == v);
    return TRUE;
}



boolean 
sparseg_adjl_u_adj_v (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
        int u, int v)
    /*
      is u adj. to v 
    */
{
    return sparseg_adjl_dir_edge_exists(V, n, A, u, v);
}


boolean 
sparseg_adjl_sub (t_ver_sparse_rep *V1, int n1, t_adjl_sparse_rep *A1,
        t_ver_sparse_rep *V2, int n2, t_adjl_sparse_rep *A2)
    /*
      test if the (V1, n1, A1) sparse graph is a subgraph of
      the (V2, n2, A2) graph
    */
{
    int             v, *fp, n, bign, i;

    n = n1 > n2 ? n2 : n1;
    bign = n1 > n2 ? n1 : 0;
    fp = (int *) mem_malloc(sizeof(int) * n);
    for (i = 0; i < n; i++)
        fp[i] = NIL;

    for (v = 0; v < n; v++)
    {
        int      ne1, ne2;

        ne1 = V1[v].first_edge;
        ne2 = V2[v].first_edge;
        if (ne1 == NIL)
        {
            continue;
        }
        else if (ne2 == NIL)
        {
            mem_free(fp);
            return FALSE;
        }
        
        while (ne2 != NIL)
        {
            int u2;

            u2 = A2[ne2].end_vertex;
            fp[u2] = v;
            ne2 = A2[ne2].next;
        }

        while (ne1 != NIL)
        {
            int u1;

            u1 = A1[ne1].end_vertex;
            if (fp[u1] != v)
            {
                mem_free(fp);
                return FALSE;
            }
            ne1 = A1[ne1].next;
        }
    }
    mem_free(fp);

    for (v = n; v < bign; v++)
        /*
          those vertices must not be end points of edges:
          this chcek is only necessary in the digraph case
        */
    {
        if (V1[v].first_edge != NIL)
            return FALSE;
    }
    
    return TRUE;
}



boolean 
sparseg_adjl_eq (t_ver_sparse_rep *V1, int n1, t_adjl_sparse_rep *A1,
        t_ver_sparse_rep *V2, int n2, t_adjl_sparse_rep *A2)
    /*
      compare the two sparse graphs (V1, n1, A1) & (V2, n2, A2)
      we don't know their number of edges
    */
{
    if (n1 != n2)
        return FALSE;

    return sparseg_adjl_sub(V1, n1, A1, V2, n2, A2)
        && sparseg_adjl_sub(V2, n2, A2, V1, n1, A1);
}



/*
 *  sparseg_dlcl_misc.c
 */
 
/*
  What:
  *****
  
  Implementing:
 
  Housekeeping for an internal sparse graph representation
  internal to the planarity tester and obstruction isolator.

  This sparse graph consists of an array of doubly linked circular lists
  (the neighbour lists for each vertex).
 
 
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********
 
  Paulette Lieby (Magma), Brendan McKay (ANU)
 
  Started October 2001
*/

#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
 
 
/* aproto: header embed_graph_protos.h */

/* aproto: beginstatic -- don't touch this!! */
static boolean sparseg_dlcl_is_present (t_dlcl *, int, t_dlcl **);
/* aproto: endstatic -- don't touch this!! */
 
 
#ifndef PLANAR_IN_MAGMA
#endif
 

void 
sparseg_dlcl_delete (t_dlcl **g, int n)
{
    int      i;

    for (i = 0; i < n; i++)
    {
       embedg_dlcl_delete(g[i]);
    }
    mem_free(g);
}

void 
sparseg_dlcl_print (t_dlcl **g, int n)
{
    int      i;

    for (i = 0; i < n; i++)
    {
        fprintf(stdout,"%d:\t", i);
        embedg_dlcl_print(g[i]);
    }
}


static boolean 
sparseg_dlcl_is_present (t_dlcl *l, int label, t_dlcl **p)
{
    *p = embedg_dlcl_find(l, label);
    return *p == NP ? FALSE : TRUE;
}


boolean 
sparseg_dlcl_is_adjacent (t_dlcl **g, int n, int v, int u, t_dlcl **p)
    /*
      is u adjacent to v
    */
{
    ASSERT(v >= 0 && v < n && u >= 0 && u < n);
    return sparseg_dlcl_is_present(g[v], u, p);
}

void 
sparseg_dlcl_append_to_neigh_list (t_dlcl **g, int n, int v, int u, int in_adjl)
    /*
      append u to the neighbour list of v
    */
{
    t_dlcl   *u_rec;
 
    u_rec = embedg_dlcl_rec_new(u);
    u_rec->in_adjl = in_adjl;
    g[v] = embedg_dlcl_rec_append(g[v], u_rec);
}




void 
sparseg_dlcl_to_sparseg (t_dlcl **g, int n, int e,
        t_ver_sparse_rep **V, t_adjl_sparse_rep **A)
    /*
      e is the number of undirected edges of g

      convert a dlcl into the standard sparseg rep. as an
      adjacency list
    */
{
    int                 i_e, v;

    *V = (t_ver_sparse_rep *) mem_malloc(sizeof(t_ver_sparse_rep) * n);
    *A = (t_adjl_sparse_rep *) mem_malloc(sizeof(t_adjl_sparse_rep) * 2 * e);
 
    for (v = 0; v < n; v++)
        (*V)[v].first_edge = NIL;
    
    i_e = 0;
    for (v = 0; v < n; v++)
    {
        t_dlcl     *l, *p;

        l = p = g[v];
        if (!embedg_dlcl_is_empty(p))
        {
            t_adjl_sparse_rep   a;
            
            ASSERT((*V)[v].first_edge == NIL);
            (*V)[v].first_edge = i_e;
            a.end_vertex = p->info;
            a.next = i_e + 1;
            (*A)[i_e++] = a;
            
            p = embedg_dlcl_list_next(p);
            while (p != l)
            {
                a.end_vertex = p->info;
                a.next = i_e + 1;
                (*A)[i_e++] = a;

                p = embedg_dlcl_list_next(p);
            }

            /*
              end of list for v
            */
            (*A)[i_e - 1].next = NIL;
        }
    }
    ASSERT(i_e == 2 * e);
}

boolean 
sparseg_dlcl_sub (t_dlcl **g1, int n1, t_dlcl **g2, int n2)
    /*
      is g2 a subgraph of g1

      I request that both graphs have same order

      This is not used anywhere... do we need it???
    */
{
    int           n, v, *fp;

    if (n1 != n2)
        return FALSE;

    n = n1;
    fp = (int *) mem_malloc(sizeof(int) * n);
    for (v = 0; v < n; v++)
        fp[v] = NIL;

    for (v = 0; v < n; v++)
    {
         t_dlcl     *l1, *p1, *l2, *p2;
 
        l1 = p1 = g1[v];
        l2 = p2 = g2[v];
        if (embedg_dlcl_is_empty(p1) && !embedg_dlcl_is_empty(p2))
        {
            mem_free(fp);
            return FALSE;
        }
        if (embedg_dlcl_is_empty(p2))
        {
            continue;
        }
        
        fp[p1->info] = v;
        p1 = embedg_dlcl_list_next(p1);
        while (p1 != l1)
        {
            fp[p1->info] = v;
            p1 = embedg_dlcl_list_next(p1);
        }

        if (fp[p2->info] != v)
        {
            mem_free(fp);
            return FALSE;
        }
        p2 = embedg_dlcl_list_next(p2);
        while (p2 != l2)
        {
            if (fp[p2->info] != v)
            {
                mem_free(fp);
                return FALSE;
            }
        }
    }
    mem_free(fp);
    
    return TRUE;
}
/*
 *  VES_misc.c
 */
 
/*
  What:
  *****
  
  Implementing:

  All low-level routines for the VES structure:

  - the VES structure is solely used within the planarity tester
    and obstruction isolator

  - it stores vertices, virtual vertices and edges
       --more on this later--

  - it allows for circular doubly linked lists, hence
    enabling us -among other things- to store the
    graph embedding if the tester is successful

  - basic features:
    + the VES has exactly size 2n + 2(3n-5) :
      we add at most one more edge than the max for a planar graph
      (need to x by 2: we store directed edges)
    + a vertex and the edges incident FROM it are linked in a doubly
      linked circular list
    + where a vertex is inserted between two of its outcoming edges
      determines an external face walk for a bicomponent
    + the twin edge is more commonly known as the inverse edge
    + we have tree and back edges (from the DFS), and short-cut edges
      which are added by the tester
      -but short-cut edges are added in such a way as to maintain
       planarity (in a local sense)
    + vertices and edges can be marked (visited for example)
    + they have an orientation which must be eventuall recovered
      and which is set in the merge_bicomp routine
    + vertices are essentially known via their DFI or DFS index
      (though their label is stored too)

     blah, blah.... later then.
     Have a look at embedg_planar_alg_init which initialises the VES
     structure

     
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_SCE(x)    {}
#define IF_DEB_PROPER_FACE(x) {}
#define IF_VERB(x)   {}
 
 
/* aproto: header embed_graph_protos.h */

boolean 
embedg_VES_is_vertex (int n, int i)
    /*
      is this a vertex
      (relative to the "big" array of size 2n + 2(3n-5))
    */
{
    return i < n ? TRUE : FALSE;
}

boolean 
embedg_VES_is_virtual_vertex (int n, int i)
    /*
      is this a virtual vertex
      (relative to the "big" array of size 2n + 2(3n-5))

      a virtual vertex is a vertex v^c which denotes the
      DFS parent of the child c

      see embedg_planar_alg_init for more
    */
{
    return i >= n && i < 2*n ? TRUE : FALSE;
}

boolean 
embedg_VES_is_edge (int n, int i)
    /*
      is this an edge
      (relative to the "big" array of size 2n + 2(3n-5))
    */
{
    return i >= 2*n ? TRUE : FALSE;
}

boolean 
embedg_VES_is_tree_edge (t_ver_edge *embed_graph, int n, int i)
    /*
      is this s tree edge
    */
{
    return embedg_VES_is_edge(n, i)
        && embed_graph[i].type == TE;
}

boolean 
embedg_VES_is_back_edge (t_ver_edge *embed_graph, int n, int i)
    /*
      is this a back edge
    */
{
    return embedg_VES_is_edge(n, i)
        && embed_graph[i].type == BE;
}

boolean 
embedg_VES_is_short_cut_edge (t_ver_edge *embed_graph, int n, int i)
    /*
      as the name indicates...
    */
{
    return embedg_VES_is_edge(n, i)
        && embed_graph[i].type == SCE;
}

void 
embedg_VES_print_vertex (int n, int v)
{
    ASSERT(embedg_VES_is_vertex(n, v));
    fprintf(stdout, "%d  ", v);
}

void 
embedg_VES_print_virtual_vertex (t_ver_edge *embed_graph, int n, int v)
{
    int          c;
    
    ASSERT(embedg_VES_is_virtual_vertex(n, v));
    c = v - n;
    fprintf(stdout, "%d^%d  ", embed_graph[c].DFS_parent, c);
}

void 
embedg_VES_print_any_vertex (t_ver_edge *embed_graph, int n, int v)
{
    if (embedg_VES_is_vertex(n, v))
    {
        embedg_VES_print_vertex(n, v);
    }
    else
    {
        embedg_VES_print_virtual_vertex(embed_graph, n, v);
    }
}

void 
embedg_VES_print_any_rec (t_ver_edge *embed_graph, int n, int r)
{
    if (embedg_VES_is_edge(n, r))
    {
        embedg_VES_print_edge(embed_graph, n, r);
    }
    else
    {
        embedg_VES_print_any_vertex(embed_graph, n, r);
    }
}

void 
embedg_VES_print_edge (t_ver_edge *embed_graph, int n, int e)
{
    int          v, prev, cur;
    
    ASSERT(embedg_VES_is_edge(n, e));

    /*
      must find the vertex in the doubly linked circular list
      of vertices/edges
    */

    prev = e;
    cur = v = embed_graph[e].link[0];
    if (embedg_VES_is_vertex(n, v)
        || embedg_VES_is_virtual_vertex(n, v))
    {
        embedg_VES_print_any_vertex(embed_graph, n, v);
        fprintf(stdout, ", ");
        embedg_VES_print_any_vertex(embed_graph, n,
                                         embed_graph[e].neighbour);
        fprintf(stdout, "):0\n");
    }
    else while (!embedg_VES_is_vertex(n, v)
                && !embedg_VES_is_virtual_vertex(n, v))
    {
        v = embedg_VES_get_next_in_dlcl(embed_graph, n,
                                                   cur, prev);

        if (embedg_VES_is_vertex(n, v)
            || embedg_VES_is_virtual_vertex(n, v))
        {
            embedg_VES_print_any_vertex(embed_graph, n, v);
            fprintf(stdout, ", ");
            embedg_VES_print_any_vertex(embed_graph, n,
                                             embed_graph[e].neighbour);
            fprintf(stdout, "):0\n");
        }
        else
        {
            prev = cur;
            cur = v;
        }
    }
}

void 
embedg_VES_print_flipped_edges (t_ver_edge *embed_graph, int n, int edge_pos)
    /*
      print those edges in the structure whose sign is CLOCKW,
      ie which have been flipped at some stage
    */
{
    int          e;
    
    for (e = 2*n; e <= edge_pos; e++)
    {
        if (!embedg_VES_is_short_cut_edge(embed_graph, n, e))
            /*
              we don't care about the short-cut edges
            */
        {
            if (embed_graph[e].sign != CCLOCKW)
            {
                embedg_VES_print_edge(embed_graph, n, e);
            }
        }
    }
}

#if 0
int 
embedg_VES_get_edge_from_ver (t_ver_edge *embed_graph, int n, int v)
    /*
      not used anywhere; why is this here???
    */
{
    int          in, e;

    ASSERT(embedg_VES_is_vertex(n, v)
           || embedg_VES_is_virtual_vertex(n, v));
    
    in = embedg_VES_is_edge(n, embed_graph[v].link[0]) ? 0 : 1;
    e = embed_graph[v].link[in];
    ASSERT(embedg_VES_is_edge(n, e));

    return e;
}

int 
embedg_VES_get_ver_from_edge (t_ver_edge *embed_graph, int n, int e)
{
    int          in, v;

    ASSERT(embedg_VES_is_edge(n, e));

    in = embedg_VES_is_vertex(n, embed_graph[e].link[0])    
        || embedg_VES_is_virtual_vertex(n, embed_graph[e].link[0])
        ?
        0 : 1;

    v = embed_graph[e].link[in];
    ASSERT(embedg_VES_is_vertex(n, v)
           || embedg_VES_is_virtual_vertex(n, v));

    return v;
}
#endif

int 
embedg_VES_get_twin_edge (t_ver_edge *embed_graph, int n, int e)
    /*
      the twin edge is understood as being the inverse edge
    */
{
    int          twin;

    ASSERT(embedg_VES_is_edge(n, e));

    twin = e % 2 == 0 ? e + 1 : e - 1;
    ASSERT(embedg_VES_is_edge(n, twin));

    return twin;
}

int 
embedg_VES_get_ver_from_virtual (t_ver_edge *embed_graph, int n, int vv)
    /*
      get v from the virtual vertex v^c
    */
{
    int          v;

    ASSERT(embedg_VES_is_virtual_vertex(n, vv));
    v = embed_graph[vv - n].DFS_parent;

    return v;
}

int 
embedg_VES_get_ver (t_ver_edge *embed_graph, int n, int v)
{
    if (embedg_VES_is_virtual_vertex(n, v))
        return embedg_VES_get_ver_from_virtual(embed_graph, n, v);

    return v;
}
    

int 
embedg_VES_get_next_in_dlcl (t_ver_edge *embed_graph, int n, int r, int prev)
    /*
      r is a (virtual) vertex or edge record in embed_graph:
      get the next in the list (formed by the .link[] fields)
      in the doubly linked circular list

      so that prev != next
      -- NOTE: a priori these lists always contain 2 elts at least
      so that there shouldn't be any problem...
      --> huh? is that true?
    */
{
    return embed_graph[r].link[0] == prev ?
        embed_graph[r].link[1] : embed_graph[r].link[0];
}


void 
embedg_VES_walk_bicomp (t_ver_edge *embed_graph, int n, int v, int vin)
    /*
      walk the external face of the bicomp starting
      at VIRTUAL vertex v entered via vin

      this of course assumes that the "thing" rooted at
      v is a bicomponent -- depending where we are at in the
      tester this is not necessarily the case
      -- I comment upon this in merge_bicomps.c:
               embedg_VES_merge_pertinent_bicomps
    */
{
    int        start, startin, s, sin;

    ASSERT(embedg_VES_is_virtual_vertex(n, v));

    embedg_VES_print_virtual_vertex(embed_graph, n, v);

    s = NIL;
    start = v;
    startin = vin;
    while (s != v)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n, start, startin,
                                             FALSE, 0, &s, &sin);
        if (embedg_VES_is_virtual_vertex(n, s))
        {
            embedg_VES_print_virtual_vertex(embed_graph, n, s);
        }
        else
        {
            embedg_VES_print_vertex(n, s);
        }
        start = s;
        startin = sin;
    }
    fprintf(stdout, "\n");
}

void 
embedg_VES_print_adj_list (t_ver_edge *embed_graph, int n, int r,
        boolean consistent)
    /*
      print r's adjacency list - r can be a vertex or edge

      the boolean <consistent> if true assumes that
      the list is consistent (will determine the way we traverse the list)

      a priori we should get the same result either way
    */
{
    if (consistent)
    {
        int        next;

        embedg_VES_print_any_rec(embed_graph, n, r);
        
        next = embed_graph[r].link[0];
        while (next != r)
        {
            embedg_VES_print_any_rec(embed_graph, n, next);
            next = embed_graph[next].link[0];
        }
    }
    else
    {
        int          prev, cur, next;
        
        embedg_VES_print_any_rec(embed_graph, n, r);
        
        prev = r;
        cur = embed_graph[r].link[0];
        
        while (cur != r)
        {
            embedg_VES_print_any_rec(embed_graph, n, cur);
            next = embedg_VES_get_next_in_dlcl(embed_graph, n,
                                                          cur, prev);
            prev = cur;
            cur = next;
        }
    }
}

boolean 
embedg_VES_is_adj_list_consistent (t_ver_edge *embed_graph, int n, int r)
    /*
      checks that r's adjacency list is consistent:
      ie, that either traversing it using link[0] always
      or traversing it using embedg_VES_get_next_in_dlcl
      gives the SAME result
    */
{
    int          *list_link, *list_n_dldl, il, id, i;

    list_link = (int *) mem_malloc(sizeof(int) * 2 * n);
    list_n_dldl = (int *) mem_malloc(sizeof(int) * 2 * n);
    /*
      must allocate 2*n space: I could have TE and SCE with same neighbour
      (or BE and SCE as well)
    */
    il = id = -1;

    /*
      traversing the list via link[0]
    */
    {
        int        next;

        list_link[++il] = r;
        
        next = embed_graph[r].link[0];
        while (next != r)
        {
            list_link[++il] = next;
            next = embed_graph[next].link[0];
        }
    }

    /*
       traversing the list using embedg_VES_get_next_in_dlcl
    */
    {
        int          prev, cur, next;
        
        list_n_dldl[++id] = r;
        prev = r;
        cur = embed_graph[r].link[0];
        
        while (cur != r)
        {
            list_n_dldl[++id] = cur;
            next = embedg_VES_get_next_in_dlcl(embed_graph, n,
                                                          cur, prev);
            prev = cur;
            cur = next;
        }
    }

    if (il != id)
    {
        mem_free(list_link);
        mem_free(list_n_dldl);
        return FALSE;
    }

    for (i = 0; i <= il; i++)
    {
        if (list_link[i] != list_n_dldl[i])
        {
            mem_free(list_link);
            mem_free(list_n_dldl);
            return FALSE;
        }
    }

    mem_free(list_link);
    mem_free(list_n_dldl);
    return TRUE;
}


boolean 
embedg_VES_are_adj_lists_consistent (t_ver_edge *embed_graph, int n)
    /*
      checks that the adjacency list of each vertex is consistent
      in the manner of embedg_VES_is_adj_list_consistent
    */
{
    int          i;

    /*
      it is enough to visit the vertices and virtual vertices only
      (I don't think it is enough to do the vertices only --??)
    */
    for (i = 0; i < 2*n; i++)
        if (!embedg_VES_is_adj_list_consistent(embed_graph, n, i))
            return FALSE;

    return TRUE;
}



void 
embedg_VES_remove_edge (t_ver_edge *embed_graph, int n, int e)
    /*
      remove edge e from the embedding
    */
{
    int          r1, r2, r1out, r2in, twin;

    ASSERT(embedg_VES_is_edge(n, e));

    IF_DEB_SCE(
               fprintf(stdout, "removing an SCE, enter\n");
               embedg_VES_print_edge(embed_graph, n, e);
               )
    
    r1 = embed_graph[e].link[0];
    r2 = embed_graph[e].link[1];

    /*
      disable e and link r1 and r2 together:
      we had r1 -> e -> r2
    */
    embed_graph[e].link[0] = embed_graph[e].link[1] = e;
    
    r1out = embed_graph[r1].link[0] == e ? 0 : 1;
    r2in = embed_graph[r2].link[0] == e ? 0 : 1;

    if (r1 == r2)
        /*
          this I think should never happen, but one never knows...
        */
    {
        embed_graph[r1].link[0] = embed_graph[r1].link[1] = r1;
    }
    else
    {
        embed_graph[r1].link[r1out] = r2;
        embed_graph[r2].link[r2in] = r1;
    }

    ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, r1));

    /*
      now we must do a similar thing for the twin
      (which must get reomved as well)
    */
    twin = embedg_VES_get_twin_edge(embed_graph, n, e);

    IF_DEB_SCE(
               fprintf(stdout, "removing an SCE, the twin\n");
               embedg_VES_print_edge(embed_graph, n, twin);
               )
    
    r1 = embed_graph[twin].link[0];
    r2 = embed_graph[twin].link[1];

    embed_graph[twin].link[0] = embed_graph[twin].link[1] = twin;

    r1out = embed_graph[r1].link[0] == twin ? 0 : 1;
    r2in = embed_graph[r2].link[0] == twin ? 0 : 1;

    if (r1 == r2)
    {
        embed_graph[r1].link[0] = embed_graph[r1].link[1] = r1;
    }
    else
    {
        embed_graph[r1].link[r1out] = r2;
        embed_graph[r2].link[r2in] = r1;
    }

    ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, r1));
}


void 
embedg_VES_set_orientation (t_ver_edge *embed_graph, int n, int *ver_orient)
    /*
      using the vertices' orientation as given in ver_orient
      we set the orientation for each edge in the adjacency list
      for each vertex

      to do this we use the field sign which is NOT needed
      anymore by the tester since by the time we call this
      function we would have finished with that bit (the tester)

      sign is only set when merging bicomps
      - even though we'll perform another walkdown when
      recovering an obstruction (if any) no bicomp merging will occur,
      so we are safe
    */
{
    int          v;

    for (v = 0; v < n; v++)
    {
        int      o, e;

        o = ver_orient[v];
        embed_graph[v].sign = o;

        e = embed_graph[v].link[0];

        while (e != v)
            /*
              just as a note: note the way I get the next in the list
              here (as opposed to using
              embedg_VES_get_next_in_dlcl):
              this is because I implicitely assume that
              the adjacency lists are consistent
              
              Also note that edges can be SCE, it doesn't really matter
              anyway (they may not have been removed yet
              -- see the way we recover the obstruction:
              embedg_mark_obstruction)
            */
        {
            embed_graph[e].sign = o;
            e = embed_graph[e].link[0];
        }       
    }
}


/*
 *  dlcl_misc.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Housekeeping for a simple doubly linked circular list:
  this is a data structure ONLY used WITHIN
  the planarity tester and obstruction isolator and is not to be
  confused with the VES structure mentionned elsewhere.

  The VES structure is an array, while the dlcl one is a list of
  pointers.

  The  dlcl is especially useful as it allows for the storage
  of an ordered list.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/


#include "planarity.h"

#define IF_DEB(x)    {}
#define IF_VERB(x)   {}


/* aproto: header embed_graph_protos.h */

/* aproto: beginstatic -- don't touch this!! */
static void embedg_dlcl_rec_free (t_dlcl *);
static void embedg_dlcl_rec_insert_right (t_dlcl *, t_dlcl *);
static void embedg_dlcl_rec_insert_left (t_dlcl *, t_dlcl *);
static void embedg_dlcl_rec_retrieve (t_dlcl *);
static void embedg_dlcl_rec_delete (t_dlcl *);
static boolean embedg_dlcl_is_singleton (t_dlcl *);
/* aproto: endstatic -- don't touch this!! */


#ifndef PLANAR_IN_MAGMA
#endif


t_dlcl *
embedg_dlcl_rec_new (int info)
    /*
      create a new record with info <info> in the global array
      to insert in the list
    */
{
    t_dlcl    *r;
 
    r = (t_dlcl *) mem_malloc(sizeof(t_dlcl));
    r->info = info;
    r->in_adjl = r->twin_in_adjl = NIL;
    r->mult = 1;
    r->right = r;
    r->left = r;
    return r;
}

static void 
embedg_dlcl_rec_free (t_dlcl *r)
    /*
      free
    */
{
    mem_free(r);
}

void 
embedg_dlcl_rec_print (t_dlcl *r)
{
    fprintf(stdout,"%d ", r->info);
}

void 
embedg_dlcl_print (t_dlcl *l)
{
    t_dlcl    *p = l;
    
    if (!embedg_dlcl_is_empty(p))
    {
        embedg_dlcl_rec_print(p);
        p = embedg_dlcl_list_next(p);
        while (p != l)
        {
            embedg_dlcl_rec_print(p);
            p = embedg_dlcl_list_next(p);
        }
    }
    fprintf(stdout,"\n");
}


static void 
embedg_dlcl_rec_insert_right (t_dlcl *l, t_dlcl *r)
{
    t_dlcl    *tmp_r, *tmp_l;

    tmp_r = l->right;
    tmp_l = tmp_r->left;

    l->right = r;
    r->right = tmp_r;

    r->left = tmp_l;
    tmp_r->left = r;
}


static void 
embedg_dlcl_rec_insert_left (t_dlcl *l, t_dlcl *r)
{
    t_dlcl    *tmp_r, *tmp_l;

    tmp_l = l->left;
    tmp_r = tmp_l->right;

    l->left = r;
    r->left = tmp_l;

    r->right = tmp_r;
    tmp_l->right = r;
}

t_dlcl *
embedg_dlcl_rec_append (t_dlcl *l, t_dlcl *r)
{
    if (embedg_dlcl_is_empty(l))
        return r;
    
    embedg_dlcl_rec_insert_left(l, r);
    return l;
}

t_dlcl *
embedg_dlcl_rec_prepend (t_dlcl *l, t_dlcl *r)
{
    if (embedg_dlcl_is_empty(l))
        return r;

    embedg_dlcl_rec_insert_left(l, r);
    return r;
}

t_dlcl *
embedg_dlcl_cat (t_dlcl *l, t_dlcl *m)
    /*
      concatenate m to the RIGHT of the end of l
      WITHOUT copying m 
    */
{
    t_dlcl    *h1, *h2, *e1, *e2;

    if (embedg_dlcl_is_empty(l))
        return m;
    if (embedg_dlcl_is_empty(m))
        return l;
    
    h1 = l;
    e1 = l->left;
    h2 = m;
    e2 = m->left;

    e1->right = h2;
    h2->left = e1;
    e2->right = h1;
    h1->left = e2;

    return l;
}

t_dlcl *
embedg_dlcl_find (t_dlcl *l, int info)
{
    t_dlcl    *p = l;
    
    if (!embedg_dlcl_is_empty(p))
    {
        if (p->info == info)
        {
            return p;
        }
        p = embedg_dlcl_list_next(p);
        while (p != l)
        {
            if (p->info == info)
            {
                return p;
            }
            p = embedg_dlcl_list_next(p);
        }
    }
    return NP;
}

t_dlcl *
embedg_dlcl_find_with_NIL_twin_in_adjl (t_dlcl *l, int info)
{
    t_dlcl    *p = l;
    
    if (!embedg_dlcl_is_empty(p))
    {
        if (p->info == info && p->twin_in_adjl == NIL)
        {
            return p;
        }
        p = embedg_dlcl_list_next(p);
        while (p != l)
        {
            if (p->info == info && p->twin_in_adjl == NIL)
            {
                return p;
            }
            p = embedg_dlcl_list_next(p);
        }
    }
    return NP;
}



static void 
embedg_dlcl_rec_retrieve (t_dlcl *r)
{
    t_dlcl    *right, *left;
 
    right = r->right;
    left = r->left;
 
    left->right = right;
    right->left = left;
 
    r->right = r;
    r->left = r;
}

static void 
embedg_dlcl_rec_delete (t_dlcl *r)
{
    embedg_dlcl_rec_retrieve(r);
    embedg_dlcl_rec_free(r);
}


t_dlcl *
embedg_dlcl_delete_first (t_dlcl *l)
    /*
      prune the list from the head:
      - set new head to right of old head
      - delete old head
    */
{
    t_dlcl    *new_head;

    ASSERT(!embedg_dlcl_is_empty(l));
    if (embedg_dlcl_is_singleton(l))
    {
        new_head = NP;
    }
    else
    {
        new_head = l->right;
    }
    embedg_dlcl_rec_delete(l);
    return new_head;
}


t_dlcl *
embedg_dlcl_delete_rec (t_dlcl *l, t_dlcl *r)
    /*
      delete r from l;
      if r == l, set new head to right of old head
    */
{
    if (r == l)
    {
        return embedg_dlcl_delete_first(l);
    }
    embedg_dlcl_rec_delete(r);
    return l;
}


boolean 
embedg_dlcl_is_empty (t_dlcl *l)
{
    return (l == NP) ? TRUE : FALSE;
}


static boolean 
embedg_dlcl_is_singleton (t_dlcl *l)
{
    return (l->right == l) ? TRUE : FALSE;
    /*
      same as l->left == l
    */
}

t_dlcl *
embedg_dlcl_list_next (t_dlcl *l)
    /*
      this assumes no choice in the direction of the walking
      (always to the right)
      -- good enough when deleting for example or when
      the direction of the walking does not matter
    */
{
    return l->right;
}
 

t_dlcl *
embedg_dlcl_list_prev (t_dlcl *l)
    /*
      this assumes no choice in the direction of the walking
      (always to the right)
    */
{
    return l->left;
}
 
t_dlcl *
embedg_dlcl_list_last (t_dlcl *l)
{
    return embedg_dlcl_list_prev(l);
}
 


void 
embedg_dlcl_delete (t_dlcl *l)
{
    if (!embedg_dlcl_is_empty(l))
    {
        while (!embedg_dlcl_is_singleton(l))
        {
            t_dlcl    *next;
 
            next = embedg_dlcl_list_next(l);
            embedg_dlcl_rec_delete(next);
        }
        embedg_dlcl_rec_delete(l);
    }
}

t_dlcl *
embedg_dlcl_copy (t_dlcl *l)
{
    t_dlcl    *p, *c;
    
    if (embedg_dlcl_is_empty(l))
        return NP;

    c = embedg_dlcl_rec_new(l->info);
    
    p = embedg_dlcl_list_next(l);
    while (p != l)
    {
        t_dlcl     *temp;

        temp = embedg_dlcl_rec_new(p->info);
        temp->in_adjl = p->in_adjl;
        temp->twin_in_adjl = p->twin_in_adjl;
        temp->mult = p->mult;
        c = embedg_dlcl_rec_append(c, temp);
        p = embedg_dlcl_list_next(p);
    }
    return c;
}


int 
embedg_dlcl_length (t_dlcl *l)
{
    t_dlcl    *p;
    int       n;
    
    if (embedg_dlcl_is_empty(l))
        return 0;

    p = embedg_dlcl_list_next(l);
    n = 1;
    while (p != l)
    {
        n++;
        p = embedg_dlcl_list_next(p);
    }
    return n;
}
/*
 *  planar_by_edge_addition.c
 */
 
/*
  What:
  *****
  
  Implementing:

  The top level for the planarity tester.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
#define IF_DEB_TREE(x)    {}
#define IF_DEB_EDGES(x) {}
#define IF_CPU(x) {}
 
 
/* aproto: header embed_graph_protos.h */


#ifndef PLANAR_IN_MAGMA
#endif
 

boolean 
sparseg_adjl_is_planar (
    t_ver_sparse_rep *V,
    int n,
    t_adjl_sparse_rep *A,        /* input sparse graph */
    int *nbr_c,        /* size of the graph, #components
                                    */
    t_dlcl ***dfs_tree,      /* a sparse graph rep. for the dfs tree
                                      -- vertices are as DFIs
                                      -- and children are ordered wrt
                                         lowpoint value
                                   */
    t_dlcl ***back_edges,    /* for each vertex v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v
                                      (vertices are given as DFIs)
                                   */
    t_dlcl ***mult_edges,    /* for each vertex v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v
                                      (vertices are given as DFIs)
                                   */
    t_ver_edge **embed_graph,    /* output graph embedding -- more on that
                                      later
                                   */
    int *edge_pos,        /* pos. in embed_graph for addition
                                      of the next edge */
    int *vr,
    int *wr         /* if graph is non planar, return
                                      the unembedded edge
                                      (where wr descendant of vr)
                                   */
)
    /*
      as the name indicates: is the graph planar?
    */
{
    int          v;

    IF_CPU(
    float      sttime; float time_to_now;
    )


    *embed_graph =
        embedg_planar_alg_init(V, n, A, nbr_c,
                               edge_pos, dfs_tree, back_edges, mult_edges);
    IF_CPU(
    sttime = time_current_user();
          )

    for (v = n - 1; v >= 0; v--)
        /*
          visit all vertices in descending DFI order
        */
    {
        t_dlcl     *be_l, *te_l, *p;

        IF_DEB(
               fprintf(stdout, "top level, vertex   %d\n", v);
               )
            
        /*
          find all the back edges [w, v] where w is a descendant of v
          and perform a walkup from w to v
          (ie determine which bicomps are pertinent)
        */
        be_l = (*back_edges)[v];
        p = be_l;

        if (!embedg_dlcl_is_empty(p))
        {
            int       w;
            
            w = p->info;
            IF_DEB(
                   fprintf(stdout, "top level, before walkup for w %d\n", w);
                   )
            embedg_walkup(*embed_graph, n, v, p);

            p = embedg_dlcl_list_next(p);
            while (p != be_l)
            {
                w = p->info;
                IF_DEB(
                       fprintf(stdout, "top level, before walkup for w %d\n", w);
                       )
                embedg_walkup(*embed_graph, n, v, p);
                
                p = embedg_dlcl_list_next(p);
            }
        }

        /*
          perform a walkdown for each tree edge [v, c], c a descendant of v
          (ie attempt to embed all back edges on the pertinent bicomps)
        */
        te_l = (*dfs_tree)[v];
        p = te_l;

        if (!embedg_dlcl_is_empty(p))
        {
            int             c, vv;
            t_merge_queue   q;

            c = p->info;
            vv = c + n;
            IF_DEB(
                   fprintf(stdout, "top level, before walkdown for c %d\n", c);
                   )
            q = embedg_walkdown(*embed_graph, n, edge_pos, vv);

            IF_DEB(
                   fprintf(stdout, "top level, after walkdown for c %d, state of edges'sign\n", c);
                   embedg_VES_print_flipped_edges(*embed_graph,
                                                       n, *edge_pos);
                   )
 
            /*
              temp only
            */
            embedg_merge_queue_delete(q);
            p = embedg_dlcl_list_next(p);
            while (p != te_l)
            {
                c = p->info;
                vv = c + n;
                IF_DEB(
                       fprintf(stdout, "top level, before walkdown for c %d\n", c);
                       )
                q = embedg_walkdown(*embed_graph, n, edge_pos, vv);

                IF_DEB(
                       fprintf(stdout, "top level, after walkdown for c %d, state of edges'sign\n", c);
                       embedg_VES_print_flipped_edges(*embed_graph,
                                                           n, *edge_pos);
                       )
 
                /*
                  temp only
                */
                embedg_merge_queue_delete(q);
                
                p = embedg_dlcl_list_next(p);
            }
        }


        /*
          check that each back edge [w, v], w a descendant of v,
          has been embedded
        */
        be_l = (*back_edges)[v];
        p = be_l;

        if (!embedg_dlcl_is_empty(p))
        {
            int       w;
            
            w = p->info;
            IF_DEB(
                   fprintf(stdout, "top level, before checking embedding for w %d\n",
                           w);
                   )
            if ((*embed_graph)[w].adjacent_to == v)
                /*
                  this edge hasn't been embedded:
                  the graph is non-planar
                */
            {
                /*
                  before returning we really want to ensure that
                  the vertices' adjacency lists are consistent
                */
                ASSERT(embedg_VES_are_adj_lists_consistent(
                                                       *embed_graph, n));

                IF_CPU(
                       fprintf(stdout, "CPU for tester only %f\n",
                               (time_current_user() - sttime));
                       )

                *vr = v;
                *wr = w;
                return FALSE;
            }

            p = embedg_dlcl_list_next(p);
            while (p != be_l)
            {
                w = p->info;
                IF_DEB(
                       fprintf(stdout, "top level, before checking embedding for w %d\n",
                               w);
                       )
                if ((*embed_graph)[w].adjacent_to == v)
                {
                    /*
                      before returning we really want to ensure that
                      the vertices' adjacency lists are consistent
                    */
                    ASSERT(embedg_VES_are_adj_lists_consistent(
                                                       *embed_graph, n));

                    IF_CPU(
                           fprintf(stdout, "CPU for tester only %f\n",
                                   (time_current_user() - sttime));
                           )

                    *vr = v;
                    *wr = w;
                    return FALSE;
                }
                
                p = embedg_dlcl_list_next(p);
            }
        }
    }
    IF_DEB_EDGES(
                 fprintf(stdout, "top level, total number of edges in embedding %d\n",
                         *edge_pos - 2 * n + 1);
                 )


    /*
      before returning we really want to ensure that
      the vertices' adjacency lists are consistent
    */
    ASSERT(embedg_VES_are_adj_lists_consistent(*embed_graph, n));

    IF_CPU(
           fprintf(stdout, "CPU for tester only %f\n",
                   (time_current_user() - sttime));
           )

    return TRUE;    
}



/*
 *  walkup.c
 */
 
/*
  What:
  *****
  
  Implementing:
 
  The walkup routine within the VES structure:

  Walking up from w where [w, v^c] is a (directed)
  back edge to be embeeding later.
  Along the way collect all the pertinent bicomps that
  will need to be merged before embedding the back edges
  to v^c.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/


#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
 
 
 
/* aproto: header embed_graph_protos.h */
 
 
#ifndef PLANAR_IN_MAGMA
#endif


void 
embedg_walkup (t_ver_edge *embed_graph, int n, int v, t_dlcl *p)
    /*
      walkup from w = p->info to v: [w, v] is a back edge where w is a DFS
      descendant of v
    */
{
    int          w, x, xin, y, yin;

    w = p->info;
    
    IF_DEB(
           fprintf(stdout, "walkup from %d to %d, enter\n", w, v);
           )
        
    embed_graph[w].adjacent_to = v;
    /*
      dirty trick to record some information about the BE [w, v]
      which will be useful at the time of creation and insertion of
      this BE: this happens in the walkdown procedure

      note that what I am doing here is safe: [w].in_adjl,
      [w].twin_in_adjl, [w].mult had no use so far since w is a vertex
      (and not an edge...)
    */
    embed_graph[w].in_adjl = p->in_adjl;
    embed_graph[w].twin_in_adjl = p->twin_in_adjl;
    embed_graph[w].mult = p->mult;
    
    /*
      set up the traversal contexts for w: one in each direction
    */
    x = w;
    xin = 1;
    y = w;
    yin = 0;

    while (x != v)
    {
        int          vz, z, c;

        IF_DEB(
               fprintf(stdout, "walkup, x %d and y %d\n", x, y);
               )
            
        if (embed_graph[x].visited == v
            || embed_graph[y].visited == v)
        {
            IF_DEB(
                   if (embed_graph[x].visited == v)
                       fprintf(stdout, "walkup, x visited\n");
                   else
                       fprintf(stdout, "walkup, y visited\n");
                   )
            break;
        }

        /*
          set x and y as visited!
        */
        embed_graph[x].visited = embed_graph[y].visited = v;
        
        vz = embedg_VES_is_virtual_vertex(n, x) ? x : NIL;
        vz = embedg_VES_is_virtual_vertex(n, y) ? y : vz;

        if (vz != NIL)
            /*
              that is, x (or y) is a virtual vertex
              -- in other words, we are set to find the root of the bicomp
              containing w, or of the bicomp r^c such that w is in the tree
              rooted by c

              consequently, by definition, vz is PERTINENT
            */
        {
            c = vz - n;
            z = embed_graph[c].DFS_parent;

            IF_DEB(
                   fprintf(stdout, "walkup, vz is virtual, %d^%d\n",
                           z, c);
                   )

            if (z != v)
                /*
                  determine if vz externally or internally active
                */
            {
                if (embed_graph[c].lowpoint < v)
                    /*
                      vz is externally active: APPEND to the list
                      of pertinent bicomps
                    */
                {
                    IF_DEB(
                           fprintf(stdout, "walkup, vz is ext. active\n");
                           )

                    embed_graph[z].pertinent_bicomp_list =
                        embedg_dlcl_rec_append(
                                embed_graph[z].pertinent_bicomp_list,
                                embedg_dlcl_rec_new(vz));
                }
                else
                    /*
                      vz is internally active: PREPEND to the list
                      of pertinent bicomps
                    */
                {
                    IF_DEB(
                           fprintf(stdout, "walkup, vz is pertinent\n");
                           )

                    embed_graph[z].pertinent_bicomp_list =
                        embedg_dlcl_rec_prepend(
                                embed_graph[z].pertinent_bicomp_list,
                                embedg_dlcl_rec_new(vz));
                }
            }
            
            /*
              continue the walkup, look if there are any other
              pertinent bicomps
              -- here "jump" to the next bicomp "up"
            */
            x = z;
            xin = 1;
            y = z;
            yin = 0;
        }
        else
            /*
              continue the traversal of the bicomp until one finds
              its (virtual) root
            */
        {
            embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                                 x, xin, FALSE, 0, &x, &xin);
            embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                                 y, yin, FALSE, 0, &y, &yin);
        }
    }
}

/*
 *  walkdown.c
 */
 
/*
  What:
  *****
  
  Implementing:

  The walkdown routine within the VES structure:

  walking down a bicomp rooted by a virtual vertex v^c
  and attempting to embed the back edges.
  This cannot be done if the walk has to stop due to the
  presence of externally active vertices on both
  the clockwise and the anticlockwise side of the bicomp.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_EMBED(x)    {}
#define IF_DEB_BE(x) {}
#define IF_DEB_SCE(x) {}
#define IF_VERB(x)   {}
 
 
 
/* aproto: header embed_graph_protos.h */
 
 
#ifndef PLANAR_IN_MAGMA
#endif
 


t_merge_queue 
embedg_walkdown (t_ver_edge *embed_graph, int n, int *edge_pos, int vv)
    /*
      walkdown from the virtual vertex:
      embed any back edges incident to vv if any
      and merge the encountered bicomps while walking down
      (very informative isn't it? :))

      ... and return the merge queue: will be useful when
      isolating the Kuratowski subgraphs
    */
{
    t_merge_queue    q;
    int              v, c, vvout;
    
    ASSERT(embedg_VES_is_virtual_vertex(n, vv));

    /*
      find v and c such that v^c = vv
    */
    c = vv - n;
    v = embed_graph[c].DFS_parent;

    IF_DEB(
           fprintf(stdout, "walkdown from %d^%d, enter\n", v, c);
           )

    IF_DEB_EMBED(
                 fprintf(stdout, "walkdown, embedding at start\n");
                 embedg_VES_print_bigcomps(embed_graph, n);
                 )
        
    /*
      create an empty merge queue
    */
    q = embedg_merge_queue_new(n);

    for (vvout = 0; vvout <= 1; vvout++)
        /*
          chose a direction for the walk, but walk in both
          directions unless a stopping vertex is encountered
          and other conditions are satisfied (see below)
        */
    {
        int    w, win;

        embedg_VES_get_succ_on_ext_face(embed_graph, n, vv, vvout ^ 1,
                                             FALSE, 0, &w, &win);

        IF_DEB(
               fprintf(stdout, "walkdown, successor (outside while loop) from %d^%d:%d is %d:%d\n",
                       embed_graph[vv-n].DFS_parent, vv-n, vvout ^ 1,
                       w, win);
               )

        while (w != vv)
            /*
              is there no danger we walk the whole way back to vv
              and that all the vertices along the walk are inactive?

              answer: no, because of the short-cut edges.

              Short-cut edges are precisely inserted to remove the inactive
              vertices from the external face (ie they are "pushed"
              to the internal face of the bicomp)
            */
        {
            if (embed_graph[w].adjacent_to == v)
                /*
                  ie there is a (directed) back edge [w, v]
                  (would have been set in the previous walkup routine):
                  embed this edge, but before that, merge all the bicomps
                  previouslsy collected 
                */
            {
                IF_DEB(
                       fprintf(stdout, "walkdown, embed BE (%d^%d:%d, %d:%d)\n",
                               embed_graph[vv-n].DFS_parent, vv - n, vvout,
                               w, win);
                       fprintf(stdout, "walkdown, queue before pulling elts\n");
                       embedg_merge_queue_print(q);
                       )
            
                while (!embedg_merge_queue_empty(q))
                {
                    int     u, uin, vu, vuout;

                    embedg_merge_queue_get(&q, &u, &uin, &vu, &vuout);

                    IF_DEB(
                           fprintf(stdout, "walkdown, pull from queue (%d:%d, %d^%d:%d)\n",
                                   u, uin,
                                   embed_graph[vu-n].DFS_parent, vu-n,
                                   vuout);
                       )

                    embedg_VES_merge_pertinent_bicomps(
                                                        embed_graph, n,
                                                        vu, vuout, u, uin);
                }
                IF_DEB_BE(
                          fprintf(stdout, "walkdown, before embed BE [%d^%d:%d, %d:%d]\n",
                                  embed_graph[vv-n].DFS_parent, vv - n,
                                  vvout, w, win);
                          embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    TRUE);
                          fprintf(stdout, "\n");
                          embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    FALSE);
                          )
                    
                embedg_VES_embed_edge(embed_graph, n, edge_pos,
                                                 BE, vv, vvout, w, win);

                IF_DEB_BE(
                          fprintf(stdout, "walkdown, after embed BE [%d^%d:%d, %d:%d]\n",
                                  embed_graph[vv-n].DFS_parent, vv - n,
                                  vvout, w, win);
                          embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    TRUE);
                          fprintf(stdout, "\n");
                          embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    FALSE);
                          )
                IF_DEB_EMBED(
                       fprintf(stdout, "walkdown, embedding after bicomp merge & back edge embedding\n");
                       embedg_VES_print_bigcomps(embed_graph, n);
                       )
                        
                /*
                  clear the adjacent_to flag
                */
                embed_graph[w].adjacent_to = n;  /* "invalid" value */
            }

            if (!embedg_dlcl_is_empty(embed_graph[w].pertinent_bicomp_list))
                /*
                  each pertinent child bicomp of w
                  (pertinent: contains active (ie more back edges to embed)
                  elts)
                  must be traversed
                  and pushed onto the queue for later bicomp merging
                */
            {
                int           vw, vwout, x, xin, y, yin, s, sin;

                IF_DEB(
                       fprintf(stdout, "walkdown, pertinent list for %d\n",
                               w);
                       embedg_dlcl_print(embed_graph[w].pertinent_bicomp_list);
                       )
                
                /*
                  get the first child in the pertinent list
                  (see how the list is built in embedg_walkup)

                  the child will eventually be removed from that list
                  when merging the bicomps, and surely
                  this bicomp (rooted at vw) will be merged (later)
                  because it is active and hence pushed on
                  the merge queue
                */

                /*
                  we can start by pushing the vertex (w, win) on
                  the merge queue
                */
                embedg_merge_queue_append_vertex(&q, embed_graph, n, w, win);

                IF_DEB(
                       fprintf(stdout, "walkdown, push 1rst 2-tuple on queue\n");
                       embedg_merge_queue_print(q);
                       )
                    
                /*
                  get the first child in the pertinent list
                */
                vw = (embed_graph[w].pertinent_bicomp_list)->info;

                IF_DEB(
                       fprintf(stdout, "walkdown, get pertinent %d^%d\n",
                               embed_graph[vw - n].DFS_parent, vw - n);
                       )
                    
                /*
                  start two walks starting at vw
                */
                embedg_VES_get_succ_active_on_ext_face(embed_graph, n,
                                                     v , vw, 1,
                                                     FALSE, 0, &x, &xin);
                embedg_VES_get_succ_active_on_ext_face(embed_graph, n,
                                                     v, vw, 0,
                                                     FALSE, 0, &y, &yin);

                /*
                  because of the trick of inserting short-cut edges
                  at previous stages, neighbours of vw are guaranteed
                  to be active
                  
                  (however I'll use the more general 
                  embedg_VES_get_succ_active_on_ext_face
                  instead of the restrictive 
                  embedg_VES_get_succ_on_ext_face
                  because  the walkdown may be used later to isolate
                  Kuratowski minors, in a situation where SCEs could have
                  been removed and thus where the successor on the
                  external face will no longer be guaranteed to be active)
                  (* actually I have decided to remove the SCE at the
                  very last moment hence the above pb
                  does not occur in the present implementation)

                  
                  it only remains to chose the next vertex where from
                  to continue the walk; the choice is made in that order:
                  - an internally active vertex
                    (incident to v via a backedge but whose lowpoint 
                    is NO less than v)
                  - a (externally active) pertinent vertex
                    (incident to v via a backedge but whose lowpoint
                    is less than v: ie which is also externally active)
                  - as a last resort, a non-pertinent externally vertex,
                    which is then a stopping vertex
                */
                IF_DEB(
                       fprintf(stdout, "walkdown, x and y: %d, %d\n", x, y);
                       )
                
                if (embedg_VES_is_ver_int_active(embed_graph, n,
                                                            v, x))
                    /*
                      x is internally active
                    */
                {
                    IF_DEB(
                           fprintf(stdout, "walkdown, x is int. active\n");
                           )
                        
                    s = x;
                    sin = xin;
                }
                else if (embedg_VES_is_ver_int_active(
                                                            embed_graph, n,
                                                            v, y))
                    /*
                      y is internally active
                    */
                {
                    IF_DEB(
                           fprintf(stdout, "walkdown, y is int. active\n");
                           )
                        
                    s = y;
                    sin = yin;
                }
                else if (embedg_VES_is_ver_pertinent(
                                                            embed_graph, n,
                                                            v, x))
                    /*
                      x is pertinent
                    */
                {
                    IF_DEB(
                           fprintf(stdout, "walkdown, x is pertinent\n");
                           )
                        
                    s = x;
                    sin = xin;
                }
                else
                    /*
                      tough luck: y may be externally active
                    */
                {
                    IF_DEB(
                           fprintf(stdout, "walkdown, tough luck\n");
                           )
                        
                    s = y;
                    sin = yin;
                }

                IF_DEB(
                       fprintf(stdout, "walkdown, succ. on pertinent bicomp is %d:%d\n", s, sin);
                       )
                        
                /*
                  set vwout to respect consistency of traversal
                */
                vwout = s == x ? 0 : 1;

                /*
                  now that we know vwout we can push (vw, vwout)
                  on the merge queue, thus completing the 4-tuple
                  (w, win, vw, vwout) describing a bicomp merge
                  to occur at a later stage
                */
                embedg_merge_queue_append_virtual_vertex(&q, embed_graph, n,
                                                         vw, vwout);

                IF_DEB(
                       fprintf(stdout, "walkdown, push on queue (%d:%d, %d^%d:%d)\n",
                               w, win, embed_graph[vw-n].DFS_parent, vw - n,
                               vwout);
                       embedg_merge_queue_print(q);
                       )
                    
                /*
                  we continue the walk
                */
                w = s;
                win = sin;
            }
            /*
              at this point, w is either inactive or externally active
              (w can't be pertinent: its pertinent bicomp list is empty,
              and the back edge [w, v], if any, has already been embedded)
            */
            else if (embedg_VES_is_ver_inactive(embed_graph, n,
                                                           v, w))
                /*
                  w is inactive: continue with the walk on the external face
                  and, insert a short cut edge so that w is removed
                  from the external face
                */
            {
                int   s, sin;

                IF_DEB(
                       fprintf(stdout, "walkdown, %d has no pertinent bicomps and is inactive\n", w);
                       )
                    
                embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                                     w, win,
                                                     FALSE, 0, &s, &sin);

                IF_DEB(
                       fprintf(stdout, "walkdown, successor from %d:%d is %d:%d\n",
                               w, win, s, sin);
                       )

                /*
                  s is the successor of w: we embed a short circuit edge
                  [vv, s] if
                  - the bicomp is externally active (to ensure that
                    at a later stage this new face gets bisected:
                    so that we don't end up with a face of degree 2
                    (parallel edges))
                  - if [s, vv] is not a back edge

                  CONSEQUENTLY, adding SCE edges
                  + does not destroy the planarity of the graph
                  + ensures that each face has degree > 2 so that
                    |E| <= 3 * |V| - 6 remains valid at all times
                  + that the space allocated to the edges in embed_graph
                    (via MAXDE(n)) is sufficient

                  NOTE:
                  the above still allows to embed a short-cut edge
                  as an edge parallel to a tree edge OR a back edge
                  (which then has been embedded previously
                  so that [w].adjacent has been cleared)
                  
                  but again, since the degree of the face will be
                  > 2, that's ok 

                  recall that c = vv - n
                */
                if (embed_graph[c].lowpoint < v
                    /*
                      bicomp rooted at vv is externally active
                    */
                    && embed_graph[s].adjacent_to != v)
                    /*
                      [s, vv] is not a back edge
                    */
                {
                    IF_DEB_SCE(
                           fprintf(stdout, "walkdown, before embed SCE [%d^%d:%d, %d:%d]\n",
                                   embed_graph[vv-n].DFS_parent, vv - n,
                                   vvout, s, sin);
                           embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    TRUE);
                           fprintf(stdout, "\n");
                           embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    FALSE);
                           
                           )

                    embedg_VES_embed_edge(embed_graph,
                                                     n, edge_pos,
                                                     SCE, vv, vvout, s, sin);
                    /*
                      note also that the addition of short cut edges
                      does not change the fact that the graph is planar
                      (when it is, so we never run into the problem
                      of creating/adding too many edges to embed-graph)
                    */
                    IF_DEB_SCE(
                           fprintf(stdout, "walkdown, after embed SCE [%d^%d:%d, %d:%d]\n",
                                   embed_graph[vv-n].DFS_parent, vv - n,
                                   vvout, s, sin);
                           embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    TRUE);
                           fprintf(stdout, "\n");
                           embedg_VES_print_adj_list(
                                                    embed_graph, n, vv,
                                                    FALSE);
                           
                           )
                    IF_DEB(
                           fprintf(stdout, "walkdown, embed SCE [%d^%d:%d, %d:%d]\n",
                                   embed_graph[vv-n].DFS_parent, vv - n,
                                   vvout, s, sin);
                           )

                }
                /*
                  continue the walk
                */
                w = s;
                win = sin;
            }
            else
                /*
                  w is non-pertinent and externally active:
                  it is a stopping vertex:
                  we stop here and see if we can walk in the other direction
                */
            {
                IF_DEB(
                       fprintf(stdout, "walkdown, %d is externally active\n", w);
                       )
                break;
            }
        }
        if (!embedg_merge_queue_empty(q))
            /*
              mumm.... don't understand this one... let's see:
              the queue constains pertinent bicomps collected during one of
              the traversal of the external face, so that once
              a stopping vertex has been encountered and the queue
              is not empty, this means that we will be unable
              to embed any remaining back edges:

              it is important to remember that when w is a stopping vertex
              there is no choice left, since we walk the pertinent
              bicomp in both directions at once, and always choose
              the "best" possible vertex
              (see the choice strategy: (a) internally active, (b) pertinent,
              (c) the rest)
            */
        {
            IF_DEB(
                   fprintf(stdout, "walkdown, merge queue is not empty\n");
                   )
            break;
        }
    }

    /*
      and return the merge queue
    */
    return q;
}




/*
 *  merge_queue_misc.c
 */
 
/*
  What:
  *****
  
  Implementing:

  The merge queue stores the pertinent bicomps waiting to
  be merged before a subsequent back edge embedding.
  See walkdown.c

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
 
 
 
/* aproto: header embed_graph_protos.h */

 
#ifndef PLANAR_IN_MAGMA
#endif

t_merge_queue 
embedg_merge_queue_new (int n)
    /*
      create a merge queue of 4 * (n-1) elts:
      we can only have at most n-1 virtual vertices,
      and for each of those we need to store 4 bits of info
    */
{
    t_merge_queue   q;

    q.start = q.end = 0;
    q.b = (int *) mem_malloc(sizeof(int) * 4 * (n - 1));

    return q;
}

void 
embedg_merge_queue_delete (t_merge_queue q)
{
    mem_free(q.b);
}


boolean 
embedg_merge_queue_empty (t_merge_queue q)
{
    return q.start == q.end ? TRUE : FALSE;
}

void 
embedg_merge_queue_print (t_merge_queue q)
{
    int        i;

    for (i = q.start; i < q.end; i++)
    {
        fprintf(stdout, "%d:%d ", q.b[i], q.b[i+1]);
        ++i;
    }
    fprintf(stdout, "\n");
}

void 
embedg_merge_queue_append (t_merge_queue *q, t_ver_edge *embed_graph,
        int n, int v, int vin, int vv, int vvout)
    /*
      append the 4-tuple (v, vin, vv, vvout)
      where v is a vertex and vv is its virtual counterpart

      we don't do much here, most of the work is done
      when pulling a bicomp/4-tuple from the queue
    */
{
    /*
      is this really necessary? - YES!!!
    */
    ASSERT((*q).end < 4 * (n - 2));
    ASSERT(embedg_VES_is_vertex(n, v));
    ASSERT(embedg_VES_is_virtual_vertex(n, vv));
    ASSERT(embed_graph[vv - n].DFS_parent == v);

    (*q).b[(*q).end++] = v;
    (*q).b[(*q).end++] = vin;
    (*q).b[(*q).end++] = vv;
    (*q).b[(*q).end++] = vvout;
}

void 
embedg_merge_queue_append_vertex (t_merge_queue *q, t_ver_edge *embed_graph,
        int n, int v, int vin)
    /*
      same as above but were we only append the 2-tuple (v, vin),
      appending the 2-tuple (vv, vvout) at a later stage
      (see embedg_merge_queue_append_virtual_vertex)
    */
{
    ASSERT((*q).end < 4 * (n - 2));
    ASSERT(embedg_VES_is_vertex(n, v));

    (*q).b[(*q).end++] = v;
    (*q).b[(*q).end++] = vin;

    IF_DEB(
           fprintf(stdout, "merge_queue_append_vertex, after, end is %d\n",
                   (*q).end);
           )
}

void 
embedg_merge_queue_append_virtual_vertex (t_merge_queue *q,
        t_ver_edge *embed_graph, int n, int vv, int vvout)
    /*
      counterpart to embedg_merge_queue_append_vertex:
      here we append the 2-tuple (vv, vvout), vv = v^c,
      where the 2-tuple (v, vin) is already in the queue
      (see embedg_merge_queue_append_vertex)
    */
{
    ASSERT(!embedg_merge_queue_empty(*q));
    ASSERT(embedg_VES_is_virtual_vertex(n, vv));
    ASSERT(embed_graph[vv - n].DFS_parent == (*q).b[(*q).end - 2]);
    
    (*q).b[(*q).end++] = vv;
    (*q).b[(*q).end++] = vvout;

    IF_DEB(
           fprintf(stdout, "merge_queue_append_virtual_vertex, after, end is %d\n",
                   (*q).end);
           )
}

void 
embedg_merge_queue_get (t_merge_queue *q, int *v, int *vin, int *vv, int *vvout)
    /*
      pulling out a 4-tuple from the beginning of the FIFO queue
    */
{
    ASSERT(!embedg_merge_queue_empty((*q)));

    *v = (*q).b[(*q).start++];
    *vin = (*q).b[(*q).start++];
    *vv = (*q).b[(*q).start++];
    *vvout = (*q).b[(*q).start++];
}

void 
embedg_merge_queue_prune (t_merge_queue *q, int *v,
                          int *vin, int *vv, int *vvout)
    /*
      pulling out a 4-tuple from the end of the FIFO queue
    */
{
    ASSERT(!embedg_merge_queue_empty((*q)));

    *vvout = (*q).b[--((*q).end)];
    *vv = (*q).b[--((*q).end)];
    *vin = (*q).b[--((*q).end)];
    *v = (*q).b[--((*q).end)];
}

/*
 *  vertex_activity.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Determining a vertex's activity. This takes place within
  the VES structure.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/
 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
 
 
 
/* aproto: header embed_graph_protos.h */
 
 
#ifndef PLANAR_IN_MAGMA
#endif
 
 


boolean 
embedg_VES_is_ver_pertinent (t_ver_edge *embed_graph, int n, int v, int w)
    /*
      is w pertinent (wrt v)
      - the field adjacent_to = v: means there is a back edge [w, v]
      - or w has a non empty pertinent_bicomp_list
    */
{
    boolean       ans;
    
    ans = embed_graph[w].adjacent_to == v ? TRUE : FALSE;

    if (ans)
        return TRUE;
    else
        return embedg_dlcl_is_empty(embed_graph[w].pertinent_bicomp_list) ?
            FALSE : TRUE;
}

boolean 
embedg_VES_is_ver_ext_active (t_ver_edge *embed_graph, int n, int v, int w)
    /*
      is w externally active (wrt v)
      this is the case when either w's least_ancestor < v
      or the first member of w's separated_DFS_child_list has lowpoint < v
      (the vertices in separated_DFS_child_list are ordered by lowpoint)

      why? because w's separated_DFS_child_list may be empty
      (due to prior bicomp merging say) and so its children are in effect
      inactive
    */
{
    boolean       ans;

    ans = embed_graph[w].least_ancestor < v ? TRUE : FALSE;

    if (ans)
        return TRUE;
    else
    {
        if (embedg_dlcl_is_empty(embed_graph[w].separated_DFS_child_list))
        {
            return FALSE;
        }
        else
        {
            int      c;
            
            c = (embed_graph[w].separated_DFS_child_list)->info;
            return embed_graph[c].lowpoint < v ? TRUE : FALSE;
        }
    }
}


boolean 
embedg_VES_is_ver_int_active (t_ver_edge *embed_graph, int n, int v, int w)
    /*
      is w internally active (wrt v):
      this happens when w is pertinent but NOT externally active
    */
{
    return embedg_VES_is_ver_pertinent(embed_graph, n, v, w)
        && !embedg_VES_is_ver_ext_active(embed_graph, n, v, w);
}

boolean 
embedg_VES_is_ver_inactive (t_ver_edge *embed_graph, int n, int v, int w)
    /*
      is w inactive (wrt v), that is w nor pertinent nor externally activ
    */
{
    return !embedg_VES_is_ver_pertinent(embed_graph, n, v, w)
        && !embedg_VES_is_ver_ext_active(embed_graph, n, v, w);
}

/*
 *  merge_bicomps.c
 */
 
/*
  What:
  *****
  
  Implementing:

  In the VES structure, merging two bicomponents.
  That is, merging the virtual vertex v^c with the
  actual vertex v while merging their respective
  adjacency lists.
  This must be done in a very specific manner so as to able to
  determine the subsequent internal/external faces.
  Also, great care must be taken so that the resulting
  adj. list for v is consistent (wrt to the direction
  of traversal).


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_ADJL(x)    {}
#define IF_VERB(x)   {}

 
 
/* aproto: header embed_graph_protos.h */


void 
embedg_VES_merge_simple_bicomps (t_ver_edge *embed_graph, int n, int vv,
        int vvout, int v, int vin)
    /*
      merge the bicomp rooted at vv (vv a virtual vertex) with
      its counterpart v so that the resulting adjacency list for v
      is consistent and is the union of the adjacency lists for vv and v

      we treat the case that the bicomp may be flipped (vvout == vin)
      here
    */
{
    int       c, edge, twin, root_edge, cur, prev;
    int       vout, vvin, e1, e2, e3, e4, e1out, e3out, e4in;

    /*
      find c such that [v^c, c] is the root edge of the bicomp
      rooted at vv = v^c
    */
    c = vv - n;
    ASSERT(embed_graph[c].DFS_parent == v);

    IF_DEB(
           fprintf(stdout, "merge_simple_bicomp, start: merge\n");
           embedg_VES_print_virtual_vertex(embed_graph, n, vv);
           fprintf(stdout, ":%d & ", vvout);
           embedg_VES_print_vertex(n, v);
           fprintf(stdout, ":%d\n", vin);
           )

    IF_DEB_ADJL(
           fprintf(stdout, "merge_simple_bicomp, adj. list for %d (before)\n", vv);
           embedg_VES_print_adj_list(embed_graph, n, vv,
                                                TRUE);
           fprintf(stdout, "\n");
           embedg_VES_print_adj_list(embed_graph, n, vv,
                                                FALSE);
           fprintf(stdout, "\n");

           fprintf(stdout, "merge_simple_bicomp, adj. list for %d (before)\n", v);
           embedg_VES_print_adj_list(embed_graph, n, v,
                                                TRUE);
           fprintf(stdout, "\n");
           embedg_VES_print_adj_list(embed_graph, n, v,
                                                FALSE);
           )
    /*
      find all edges incident to vv and (re)set all references
      to incidence to vv to incidence to v

      by the same token, find the root_edge [v^c, c]

      MOREVOVER, when vin == vvout, the bicomp (rooted by v^v = vv)
      will be flipped:
      we must invert the links of all the edges incident
      to vv so that their further union with v's adjacency list
      results in a consistent adjacency list for v!

      we do everything in one go
    */

    /*
      very careful here: a root edge must ALSO be a TE
      (because the same edge could have been added as a SCE)
    */
        
    root_edge = NIL;
    edge = embed_graph[vv].link[vvout];
    ASSERT(embedg_VES_is_edge(n, edge));
    if (embed_graph[edge].neighbour == c
        && embedg_VES_is_tree_edge(embed_graph, n, edge))
    {
        root_edge = edge;
    }

    if (vin == vvout)
        /*
          invert the links
        */
    {
        int  in, out;

        in = embed_graph[edge].link[0];
        out = embed_graph[edge].link[1];
        embed_graph[edge].link[0] = out;
        embed_graph[edge].link[1] = in;
    }
    /*
      get the twin and set the neighbour there to v (was vv originally)
    */
    twin =  embedg_VES_get_twin_edge(embed_graph, n, edge);
    ASSERT(embed_graph[twin].neighbour == vv);
    embed_graph[twin].neighbour = v;

    prev = vv;
    cur = edge;
    while (edge != vv)
    {
        edge =
            embedg_VES_get_next_in_dlcl(embed_graph, n,
                                                   cur, prev);

        if (embedg_VES_is_edge(n, edge))
            /*
              get the twin again (and invert the links if need be)
            */
        {
            if (embed_graph[edge].neighbour == c
                && embedg_VES_is_tree_edge(embed_graph, n, edge))
            {
                root_edge = edge;
            }

            if (vin == vvout)
            {
                int  in, out;
                
                in = embed_graph[edge].link[0];
                out = embed_graph[edge].link[1];
                embed_graph[edge].link[0] = out;
                embed_graph[edge].link[1] = in;
            }
            
            twin =
                embedg_VES_get_twin_edge(embed_graph, n, edge);
            ASSERT(embed_graph[twin].neighbour == vv);
            embed_graph[twin].neighbour = v;

            prev = cur;
            cur = edge;
        }
        else
        {
            ASSERT(edge == vv);
            /*
              only one vertex in the whole circular list
            */
        }
    }
    ASSERT(root_edge != NIL);

    /*
      and now union the adjacency lists of v and vv:

      let e1 be the edge record used to enter v
          e2                            exit  v
          e3                            enter vv 
          e4                            exit  vv :

          e1 -> v  -> e2
          e3 -> vv -> e4

      the union of the list is done in such a way that
      - e1 and e4 are consecutive in v's adjacency list:
        they are now in the internal face
      - e3 is now the edge record used to enter v:
        it is on the external face (along with e2) :

          e1 -> e4
          e3 -> v -> e2

      (note that this does not assume that e1 & e2 are distinct
      or that e3 & e4 are distinct)
    */
    /*
      I must not forget the case where v is a lone vertex:
      this is the case where v has no DFS ancestor, ie when
      v is the root of a tree in the DFS forest
    */

    e1 = embed_graph[v].link[vin];
    vout = 1 ^ vin;
    e2 = embed_graph[v].link[vout];

    if (e1 != v)
    {
        ASSERT(e2 != v);
        ASSERT(embedg_VES_is_edge(n, e1));
        ASSERT(embedg_VES_is_edge(n, e2));
    }
    
    e4 = embed_graph[vv].link[vvout];
    ASSERT(embedg_VES_is_edge(n, e4));

    vvin = 1 ^ vvout;
    e3 = embed_graph[vv].link[vvin];
    ASSERT(embedg_VES_is_edge(n, e3));
    
    /*
      must take care of the adjacency list's consistency of traversal
      (will be important only when recovering the embedding)
    */
    if (e1 == e2)
    {
        ASSERT(embed_graph[e1].link[0] == embed_graph[e1].link[1]);
        if (vin == vvout)
            /*
              the bicomp will be flipped:
              must take 1 ^ vvout - difficult to explain -- later...
            */
        {
            e1out = 1 ^ vvout;
        }
        else
        {
            e1out = vvout;
        }
    }
    else
    {
        e1out = embed_graph[e1].link[0] == v ? 0 : 1;
    }
    if (e3 == e4)
    {
        ASSERT(embed_graph[e3].link[0] == embed_graph[e3].link[1]);
        e3out = 1 ^ vin;
        e4in = vin;
    }
    else
    {
        e4in = embed_graph[e4].link[0] == vv ? 0 : 1;
        e3out = embed_graph[e3].link[0] == vv ? 0 : 1;
    }

    IF_DEB(
           fprintf(stdout, "merge_simple_bicomp, before union of lists, e1\n");
           embedg_VES_print_edge(embed_graph, n, e1);
           fprintf(stdout, "merge_simple_bicomp, e3\n");
           embedg_VES_print_edge(embed_graph, n, e3);
           fprintf(stdout, "merge_simple_bicomp, e4\n");
           embedg_VES_print_edge(embed_graph, n, e4);
           )

    /*
      make e1 and e4 consecutive in the adjacency list
    */
    embed_graph[e1].link[e1out] = e4;
    embed_graph[e4].link[e4in] = e1;
    embed_graph[e3].link[e3out] = v;
    embed_graph[v].link[vin] = e3;

    IF_DEB(
           fprintf(stdout, "merge_simple_bicomp, after union of lists, e1\n");
           embedg_VES_print_edge(embed_graph, n, e1);
           fprintf(stdout, "merge_simple_bicomp, e3\n");
           embedg_VES_print_edge(embed_graph, n, e3);
           fprintf(stdout, "merge_simple_bicomp, e4\n");
           embedg_VES_print_edge(embed_graph, n, e4);
           )

    /*
      also, want to "disable" vv links, meaning then that
      vv is no longer a root of a bicomp
    */
    embed_graph[vv].link[0] = embed_graph[vv].link[1] = vv;

    IF_DEB_ADJL(
           fprintf(stdout, "merge_simple_bicomp, adj. list for %d (after)\n", vv);
           embedg_VES_print_adj_list(embed_graph, n, vv,
                                                TRUE);
           fprintf(stdout, "\n");
           embedg_VES_print_adj_list(embed_graph, n, vv,
                                                FALSE);
           fprintf(stdout, "\n");

           fprintf(stdout, "merge_simple_bicomp, adj. list for %d (after)\n", v);
           embedg_VES_print_adj_list(embed_graph, n, v,
                                                TRUE);
           fprintf(stdout, "\n");
           embedg_VES_print_adj_list(embed_graph, n, v,
                                                FALSE);
           )

    ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, v));

    /*
      finally, give an orientation to the (formerly) root edge [vv, c]
      to keep traversal consistent (when recovering embedding)
    */
    if (vin == vvout)
        /*
          flip: set the sign of the root edge to clockwise

          note: a bicomp is merged only once, so there is no need to
          "flip" the root_edge's sign: it is set once at initialisation
          and then changed here if need be.
        */
    {
        embed_graph[root_edge].sign = CLOCKW;

        IF_VERB(
            fprintf(stdout, "merge_simple_bicomp, flip for %d, sign is now %d for %d of type %d\n",
                    c, embed_graph[root_edge].sign, root_edge, embed_graph[root_edge].type);
            embedg_VES_print_edge(embed_graph, n, root_edge);
            )
    }
}



void 
embedg_VES_merge_pertinent_bicomps (t_ver_edge *embed_graph, int n,
        int vv, int vvout, int v, int vin)
    /*
      the bicomps to be merged are pertinent: on top (and before)
      performing a simple merge, there are several things to do
      related to the merging to pertinent bicomps
    */
{
    /*
      a note of caution:
      it is (very) likely that after a bicomp merge the resulting
      bicomp is not biconnected (and hence traversal of the external face
      of the bicomp via embedg_VES_get_succ_on_ext_face is non-sensical)

      remembering that a PERTINENT bicomp merge is ALWAYS followed
      by a back edge embedding we see that the end result is then a bicomp
      where again traversal of the external face
      via embedg_VES_get_succ_on_ext_face will make sense
    */
    t_dlcl    *pertinent_list, *head, *rep_in_parent_list, *parent_list;
    int       c;

    /*
      find c such that [v^c, c] is the root edge of the bicomp
      rooted at vv = v^c
    */
    c = vv - n;
    ASSERT(embed_graph[c].DFS_parent == v);

    /*
      two things to do first:
      - remove vv from head of pertinent_bicomp_list of v
      - remove c from separated_DFS_child_list of v

      one may ask the point of this since the separated_DFS_child_list
      seems to mirror pertinent_bicomp_list: but this is not exactly so:
      + pertinent_bicomp_list is ordered according to the activity
        of the (virtual) vertices
      + separated_DFS_child_list is ordered according to the vertices'
        lowpoint values
      in effect, it could (almost?*) be said that these two lists
      are in reverse order (the *almost bit would warrant some thinking here)
    */
    
    /*
      remove vv from head of pertinent_bicomp_list of v
    */
    pertinent_list = head = embed_graph[v].pertinent_bicomp_list;
    ASSERT(!embedg_dlcl_is_empty(pertinent_list));
    ASSERT(head->info == vv);

    IF_DEB(
           fprintf(stdout, "merge_pertinent_bicomp, start: merge\n");
           embedg_VES_print_virtual_vertex(embed_graph, n, vv);
           fprintf(stdout, ":%d & ", vvout);
           embedg_VES_print_vertex(n, v);
           fprintf(stdout, ":%d\n", vin);
           )

    IF_DEB(
           fprintf(stdout, "merge_pertinent_bicomp, pertinent bicomp_list of %d (before)\n", v);
           embedg_dlcl_print(embed_graph[v].pertinent_bicomp_list);
           )

    
    embed_graph[v].pertinent_bicomp_list =
        embedg_dlcl_delete_first(pertinent_list);

    IF_DEB(
           fprintf(stdout, "merge_pertinent_bicomp, pertinent bicomp_list of %d (after)\n", v);
           embedg_dlcl_print(embed_graph[v].pertinent_bicomp_list);
           )

    /*
      vv = v^c: remove c from separated_DFS_child_list of v
    */
    rep_in_parent_list = embed_graph[c].rep_in_parent_list;
    ASSERT(!embedg_dlcl_is_empty(rep_in_parent_list));

    parent_list = embed_graph[v].separated_DFS_child_list;
    ASSERT(!embedg_dlcl_is_empty(parent_list));
    embed_graph[v].separated_DFS_child_list = 
        embedg_dlcl_delete_rec(parent_list, rep_in_parent_list);

    /*
      that's it, it remains to merge, ie. union the adjacency list,
      and flipping the bicomp if necessary
    */
    embedg_VES_merge_simple_bicomps(embed_graph, n,
                                               vv, vvout, v, vin);
}




    
    
/*
 *  embed_edge.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Embedding an edge so that it lies on the external face of a bicomp.
  We work here with the VES structure.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_EMBED(x)    {}
#define IF_VERB(x)   {}
 
 
/* aproto: header embed_graph_protos.h */
 

void 
embedg_VES_embed_edge (t_ver_edge *embed_graph, int n, int *edge_pos,
        int edge_type, int vv, int vvout, int w, int win)
    /*
      embed the edge (vv, w) (vv a virtual vertex, w a vertex) between
      vv and the edge vvout
      and the edge win and w

      so that after the embedding, one exits vv via (vv, w) and
      enters w via the twin (w, vv)
    */
{
    int       temp, tempin, tempout;

    ASSERT(edge_type == BE || edge_type == SCE);
    ASSERT(embedg_VES_is_virtual_vertex(n, vv));
    ASSERT(embedg_VES_is_vertex(n, w));

    IF_DEB(
           fprintf(stdout, "embed_edge, (%d:%d)\n", vv, w);
           )
    
    /*
      first, set the edge [vv, w] with the appropriate info

      when [vv, w] is a back edge there is some more work to do
      (see the walkup procedure for the extra information we need
      to copy here
    */
    (*edge_pos)++;
    ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
    embed_graph[*edge_pos].neighbour = w;
    embed_graph[*edge_pos].type = edge_type;
    embed_graph[*edge_pos].sign = CCLOCKW;
    if (edge_type == BE)
    {
        ASSERT(embed_graph[w].adjacent_to ==
               embed_graph[vv - n].DFS_parent);

        /*
          PLUS: originally when the back edge [w, vv] was
          created (in the dfs preprocessing stage), it carried in
          .in_adjl the index of this directed edge in the
          adjacency list

          but now, note that we are actually inserting the
          directed edge [vv, w] in vv's adjacency list,
          meaning that in_adjl and twin_in_adjl
          must be exchanged!
        */
        embed_graph[*edge_pos].in_adjl = embed_graph[w].twin_in_adjl;
        embed_graph[*edge_pos].twin_in_adjl = embed_graph[w].in_adjl;

        ASSERT(embed_graph[w].mult % 2 == 0);
        /*
          the original graph is always undirected:
          we store its number of undirected edges
        */
        embed_graph[*edge_pos].mult = embed_graph[w].mult / 2;
    }
        
    /*
      insert this edge between vertex record for vv
      and edge record vv.link[vvout]
    */
    temp = embed_graph[vv].link[vvout];
    
    if (embed_graph[temp].link[0] == embed_graph[temp].link[1])
        /*
          this needs special treatment to ensure consistency of
          orientation
        */
    {
        ASSERT(embed_graph[temp].link[0] == vv);
        tempin = 1 ^ vvout;
    }
    else
    {
        tempin = embed_graph[temp].link[0] == vv ? 0 : 1;
    }

    IF_DEB(
           fprintf(stdout, "embed_edge, edge out of vv\n");
           embedg_VES_print_edge(embed_graph, n, temp);
           )
    
    embed_graph[vv].link[vvout] = *edge_pos;
    embed_graph[temp].link[tempin] = *edge_pos;
    /*
      the links for *edge_pos must also be "consistent"
    */
    embed_graph[*edge_pos].link[vvout] = temp;
    embed_graph[*edge_pos].link[vvout ^ 1] = vv;

    /*
      now create/set the twin edge, the directed edge [w, vv]
    */
    (*edge_pos)++;
    ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
    embed_graph[*edge_pos].neighbour = vv;
    embed_graph[*edge_pos].type = edge_type;
    embed_graph[*edge_pos].sign = CCLOCKW;
    if (edge_type == BE)
    {
        embed_graph[*edge_pos].in_adjl = embed_graph[w].in_adjl;
        embed_graph[*edge_pos].twin_in_adjl = embed_graph[w].twin_in_adjl;
        embed_graph[*edge_pos].mult = embed_graph[w].mult / 2;
    }
 
    /*
      and insert the twin edge between edge record w.link[win]
      and vertex record for w
    */
    temp = embed_graph[w].link[win];

    if (embed_graph[temp].link[0] == embed_graph[temp].link[1])
        /*
          again, special treatment to ensure consistency of orientation
        */
    {
        ASSERT(embed_graph[temp].link[0] == w);
        tempout = 1 ^ win;
    }
    else
    {
        tempout = embed_graph[temp].link[0] == w ? 0 : 1;
    }
    
    IF_DEB(
           fprintf(stdout, "embed_edge, edge in of w\n");
           embedg_VES_print_edge(embed_graph, n, temp);
           )
    
    embed_graph[w].link[win] = *edge_pos;
    embed_graph[temp].link[tempout] = *edge_pos;
    /*
      and consistent orientation
    */
    embed_graph[*edge_pos].link[win] = temp;
    embed_graph[*edge_pos].link[win ^ 1] = w;
}



void 
embedg_VES_add_edge (t_ver_edge *embed_graph, int n, int *edge_pos,
        int v, int w, boolean MARK, int mark)
    /*
      add the edge (v, w): this is DIFFERENT from
      embedg_VES_embed_edge in the sense
      that the present function will only be used
      when building the Kuratowski homeomorphs:

      that is, we are in a situation where the graph is NON planar

      consequently it doesn't matter much where in the adjacency
      lists of v & w the edge is added:
      let's say that we always add it at the beginning

      for our sanity's sake, we'll ensure that the resulting
      adjacency lists remain consistent!
      
      and we add the edge as a BE!
      PLUS we mark it with mark in MARK true
    */
{
    int       temp;

    ASSERT(embedg_VES_is_vertex(n, v) ||
           embedg_VES_is_virtual_vertex(n, v));
    ASSERT(embedg_VES_is_vertex(n, w) ||
           embedg_VES_is_virtual_vertex(n, w));

    IF_DEB(
           fprintf(stdout, "add_edge, (%d:%d)\n", v, w);
           )

    /*
      not sure this is the best place to do this: mark the endpoints
    */
    if (MARK)
    {
        embed_graph[v].visited = mark;
        embed_graph[w].visited = mark;
    }
        
    /*
      first, set the edge [v, w] with the appropriate info
    */
    (*edge_pos)++;
    ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
    embed_graph[*edge_pos].neighbour = w;
    embed_graph[*edge_pos].type = BE;
    /*
      the edge's orientation will be the same as the vertex
    */
    embed_graph[*edge_pos].sign = embed_graph[v].sign;
    /*
      and mark the edge
    */
    if (MARK)
    {
        embed_graph[*edge_pos].visited = mark;
    }
        
    /*
      insert this edge between vertex record for v 
      and edge record v.link[1]
    */
    temp = embed_graph[v].link[1];

    IF_DEB(
           fprintf(stdout, "add_edge, edge out of v\n");
           embedg_VES_print_edge(embed_graph, n, temp);
           )
    
    embed_graph[v].link[1] = *edge_pos;
    embed_graph[temp].link[0] = *edge_pos;
    /*
      the links for *edge_pos must also be "consistent"
    */
    embed_graph[*edge_pos].link[1] = temp;
    embed_graph[*edge_pos].link[0] = v;

    /*
      now create/set the twin edge, the directed edge [w, v]
    */
    (*edge_pos)++;
    ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
    embed_graph[*edge_pos].neighbour = v;
    embed_graph[*edge_pos].type = BE;
    embed_graph[*edge_pos].sign = embed_graph[w].sign;
    if (MARK)
    {
        embed_graph[*edge_pos].visited = mark;
    }
 
    /*
      insert this edge between vertex record for w
      and edge record w.link[1]
    */
    temp = embed_graph[w].link[1];
    
    IF_DEB(
           fprintf(stdout, "add_edge, edge out of w\n");
           embedg_VES_print_edge(embed_graph, n, temp);
           )
    
    embed_graph[w].link[1] = *edge_pos;
    embed_graph[temp].link[0] = *edge_pos;
    /*
      and consistent orientation
    */
    embed_graph[*edge_pos].link[1] = temp;
    embed_graph[*edge_pos].link[0] = w;
}


/*
 *  recover.c
 */
 
/*
  What:
  *****
  
  Implementing:

  From the VES data structure recover either the embedding ot
  the obstruction into the

  t_sparseg_ver_struct,
  t_sparseg_adjl_struct,
  t_sparseg_embed_struct
  
  data types.


  (This is no even quite true: for some obscure reason
  I recover the obstruction as a dlcl[] structure to be
  converted later.
  The obvious reason being that it is easier to check as such.
  Maybe I leave it as it is...)

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_EMBED_MULT(x) {}
#define IF_DEB_EMBED_LOOPS(x) {}
#define IF_DEB_EMBED(x)    {}
#define IF_DEB_CHECK_EMBED(x)    {}
#define IF_DEB_FACES(x) {}
#define IF_VERB(x)   {}
#define IF_DEB_SCE(x) {}
#define IF_DEB_OBS(x) {}
#define IF_DEB_CHECK_OBS(x) {}
#define IF_CPU(x) {}
 


/* aproto: header embed_graph_protos.h */

/* aproto: beginstatic -- don't touch this!! */
static void embedg_recover_embedding_embed_mult
 (t_dlcl **, t_embed_sparse_rep *, int, int, int, int, int *, boolean *, int *);
static void embedg_recover_embedding_embed_loops
 (t_dlcl **, t_embed_sparse_rep *, int, int, int *, boolean *);
static t_dlcl **embedg_get_reduced_obs (t_dlcl **, int);
static boolean embedg_is_red_obs_K33 (t_dlcl **, int);
static boolean embedg_is_red_obs_K5 (t_dlcl **, int);
/* aproto: endstatic -- don't touch this!! */
 
 
#ifndef PLANAR_IN_MAGMA
#endif

void 
embedg_recover_embedding (
    t_ver_sparse_rep *V,
    t_adjl_sparse_rep *A,             /* input (original sparse graph) */
    t_ver_edge *embed_graph,
    int n,
    int nbr_e,
    t_dlcl **mult_edges,
    t_ver_sparse_rep **vertices,
    t_embed_sparse_rep **embedding
)
    /*
      recover the embedding
      to prepare for the final Magma type for sparse & embedded graph

      we assume that all vertices/edges have been given their
      orientation
 
      at this stage we also embed the multiple edges and loops
      which were set aside in mult_edges by
      sparseg_adjl_dfs_preprocessing:

      as it turns out the last bit is pretty hairy!
    */
{
    /*
      the idea is to return an array of vertices and an array
      representing the embedding
      (careful: need to weedout the SCE)

      vertices: (*vertices)[i].first_edge contains index
                to first edge in embedding

      embedding: a doubly linked circular list of edges,
                 for each record/edge e = (*embedding)[i]:
                 e.in_adjl: index in A of e
                 e.next:    next edge in CLOCKW
                            (as an index in the embedding)
                 e.prev:    previous edge in CLOCKW
                            (as an index in embedding)
                 e.inv:     inverse edge (as an index in embedding)
                 e.mark:    a mark for this edge

      let's say that this new array is a slimmed down version of embed_graph

      one issue to address:
      - for edge e, find its index in A: this should be found
        in either the embed_graph[v] record of the mult_edges[v] record
    */
    int          index_embed, v, mult, w, v_w_in_embed, new_first_edge;
    boolean      set_next;

    IF_DEB(
           fprintf(stdout, "in recover emb.\n");
           sparseg_dlcl_print(mult_edges, n);
           );
    
    *vertices = (t_ver_sparse_rep *)
        mem_malloc(sizeof(t_ver_sparse_rep) * n);
    *embedding = (t_embed_sparse_rep *)
        mem_malloc(sizeof(t_embed_sparse_rep) * 2 * nbr_e);

    index_embed = 0;
    set_next = TRUE;
    for (v = 0; v < n; v++)
    {
        int       v_l, orient, in, out, e, cur_e, next_e;

        /*
          we take v's label
        */
        v_l = embed_graph[v].label;

        /*
          first let's deal with the isolated vertex case: those
          that refer to self
        */
        if (embed_graph[v].link[0] == v)
        {
            int       temp_index_embed;
            
            ASSERT(embed_graph[v].link[1] == v);

            /*
              there may be [v, v] loops for this vertex, must check this
            */
            temp_index_embed = index_embed - 1;
            /*
              temp_index_embed is pre-increased below
            */
            embedg_recover_embedding_embed_loops(mult_edges, *embedding,
                                                 nbr_e, v,
                                                 &temp_index_embed,
                                                 &set_next);
            
            if (temp_index_embed > index_embed - 1)
                /*
                  must fix beginning and end of adjacency list:
                */
            {
                (*vertices)[v_l].first_edge = index_embed;
                (*embedding)[temp_index_embed].next =
                    (*vertices)[v_l].first_edge;
                (*embedding)[(*vertices)[v_l].first_edge].prev =
                    temp_index_embed;

                index_embed = temp_index_embed;
                index_embed += 1;
            }
            else
            {
                (*vertices)[v_l].first_edge = NIL;
            }
            continue;
        }
        
        /*
          get v's orientation, and from this decide the way in which
          v's adjacency list will be traversed
          (recall that the list is supposed to be consistent, so no bad
          surprises)
        */
        orient = embed_graph[v].sign;
        in = orient == CCLOCKW ? 0 : 1;
        out = 1 ^ in;

        e = embed_graph[v].link[out];
        while (embedg_VES_is_short_cut_edge(embed_graph, n, e))
        {
            e = embed_graph[e].link[out];
        }
        ASSERT(embedg_VES_is_edge(n, e)
               && !embedg_VES_is_short_cut_edge(embed_graph, n, e));
        /*
          strictly speaking there should be no SCEs left at this stage...
          
          if there are SCEs in v's list, it must be the case that
          the list also contains tree or back edges...
        */
        
        (*vertices)[v_l].first_edge = index_embed;

        IF_DEB_EMBED(
                     fprintf(stdout, "recov. embed. DFI %d vertex %d at %d (edges) and %d (embedding)\n",
                             v, v_l, index_e, (*vertices)[v_l].first_edge);
                     )

        cur_e = e;
        while (TRUE)
        {
            next_e = embed_graph[cur_e].link[out];
            while (embedg_VES_is_short_cut_edge(embed_graph, n, next_e))
            {
                next_e = embed_graph[next_e].link[out];
            }
            ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, next_e));

            if (next_e == v)
                /*
                  end of adjacency list
                */
            {
                break;
            }
            
            ASSERT(embedg_VES_is_edge(n, next_e));

            (*embedding)[index_embed].in_adjl = embed_graph[cur_e].in_adjl; 
            (*embedding)[index_embed].next = index_embed + 1; /* next in adj.
                                                                list */
            (*embedding)[index_embed].mark = NIL;  /* mark */
            
            /*
              cur_e's twin is trickier:
              we'll use twin's label field to store cur_e's index in
              the embedding

              if cur_e's label != NIL this means that cur_e's twin
              is already stored in edges/embedding  and consequently
              that cur_e.label = index of its twin (in the embedding)

              note that it is safe to do so since an edge's label
              has no meaning
            */
            if (embed_graph[cur_e].label != NIL)
            {
                (*embedding)[index_embed].inv = embed_graph[cur_e].label;

                /*
                  but fix the twin by the same token
                */
                (*embedding)[embed_graph[cur_e].label].inv = index_embed;
                ASSERT((*embedding)[embed_graph[cur_e].label].in_adjl ==
                       embed_graph[cur_e].twin_in_adjl);
            }
            else
                /*
                  we store cur_e's index in the embedding in twin's label
                */
            {
                int      twin;
                
                twin = embedg_VES_get_twin_edge(embed_graph, n, cur_e);
                embed_graph[twin].label = index_embed;
            }

            /*
              so the only thing we couldn't update yet is
              (*embedding)[index_embed].prev, cur_e previous edge in the list

              but we can do this for next_e
            */
            (*embedding)[index_embed + 1].prev = index_embed;

            /*
              we check if there are any multiple edges or loops
              to embed
            */
            w = embed_graph[cur_e].neighbour;
            mult = embed_graph[cur_e].mult - 1;
            /*
              one was for the TE or BE edge
            */

            if (index_embed == (*vertices)[v_l].first_edge)
                /*
                  when looking for multiple edges/loops
                  we must temporarily "close" this ordered
                  list of vertices when in presence of the first
                  edge in the list:

                  not doing this would mean that
                  (*embedding)[(*vertices)[v_l].first_edge].prev
                  contains some irrelevant value which may cause
                  (major) trouble when embedding inverses of
                  multiple edges...
                */
            {
                (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
            }
            
            embedg_recover_embedding_embed_mult(mult_edges, *embedding,
                                                nbr_e, v, w, mult,
                                                &index_embed, &set_next,
                                                &new_first_edge);
            embedg_recover_embedding_embed_loops(mult_edges, *embedding,
                                                 nbr_e, v, &index_embed,
                                                 &set_next);
            set_next = TRUE;

            /*
              yes, it may be the case that (*vertices)[v_l].first_edge
              change while in embedg_recover_embedding_embed_mult
              -- see that function for more
            */
            (*vertices)[v_l].first_edge = new_first_edge == NIL ?
                (*vertices)[v_l].first_edge : new_first_edge;
            
            /*
              that's all, we proceed to read a new edge in the list
            */
            index_embed += 1;
            cur_e = next_e;
        }

        /*
          now next_e = v so that cur_e is the last edge in v's adjacency list
          we must deal with this case separately
        */

        /*
          fix cur_e in embedding (and its twin)
        */
        (*embedding)[index_embed].in_adjl = embed_graph[cur_e].in_adjl;

        /*
          we temporarily set next of cur_e in to index_embed + 1
        */
        (*embedding)[index_embed].next = index_embed + 1;
        (*embedding)[index_embed].mark = NIL;  /* mark */

        /*
          fix cur_e's twin
        */
        if (embed_graph[cur_e].label != NIL)
        {
            (*embedding)[index_embed].inv = embed_graph[cur_e].label;
            (*embedding)[embed_graph[cur_e].label].inv = index_embed;
            ASSERT((*embedding)[embed_graph[cur_e].label].in_adjl ==
                   embed_graph[cur_e].twin_in_adjl);
        }
        else
        {
            int      twin;
            
            twin = embedg_VES_get_twin_edge(embed_graph, n, cur_e);
            embed_graph[twin].label = index_embed;
        }

        /*
          we temporarily set the next record's prev field:
          but we can do that only if we haven't processed
          all the edges yet
        */
        if (index_embed < 2 * nbr_e - 1)
        {
            (*embedding)[index_embed + 1].prev = index_embed;

            /*
              again, check if there are any multiple edges/loops
              to embed
            */
            w = embed_graph[cur_e].neighbour;
            mult = embed_graph[cur_e].mult - 1;
            /*
              one was for the TE or BE edge
            */
            v_w_in_embed = index_embed;

            if (index_embed == (*vertices)[v_l].first_edge)
                /*
                  same comment as above
                */
            {
                (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
            }
            
            embedg_recover_embedding_embed_mult(mult_edges, *embedding,
                                                nbr_e, v, w, mult,
                                                &index_embed, &set_next,
                                                &new_first_edge);
            embedg_recover_embedding_embed_loops(mult_edges, *embedding,
                                                 nbr_e, v, &index_embed,
                                                 &set_next);

            /*
              same comment as above
            */
             (*vertices)[v_l].first_edge = new_first_edge == NIL ?
                (*vertices)[v_l].first_edge : new_first_edge;
         }

        /*
          to finish off, we must set:
          
          cur_e's next field:
          next of cur_e in the list is ... vertices[v_l].first_edge
          
          cur_e's next's previous field...
        */
        if (set_next)
            /*
              set_next (poorly named) is used to indicate which
              edges must be updated to "close off" the list:

              if set_next is TRUE, we are in the standard case
              where the last edge in the ordered adj. list
              is at index_embed

              if set_next is FALSE, the last edge in the ordered adj. list
              is at v_w_in_embed: because it could have happened
              (in embedg_recover_embedding_embed_mult only)
              that the edges have been "wedged" between
              v_w_in_embed.prev and v_w_in_embed,
              leaving v_w_in_embed the last in the list
            */
        {
            (*embedding)[index_embed].next = (*vertices)[v_l].first_edge;
            (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
        }
        else
        {
            (*embedding)[v_w_in_embed].next = (*vertices)[v_l].first_edge;
            (*embedding)[(*vertices)[v_l].first_edge].prev = v_w_in_embed;
        }
        set_next = TRUE;
            
        /*
          a simple check
        */
        ASSERT(embedg_dlcl_is_empty(mult_edges[v]));
        
        /*
          we can process another vertex
        */
        index_embed += 1;
    }
    /*
      when this is done there are a few things that must hold
    */
    ASSERT(index_embed == 2 * nbr_e);
}


static void 
embedg_recover_embedding_embed_mult (t_dlcl **mult_edges,
        t_embed_sparse_rep *embedding, int nbr_e, int v, int w,
        int mult, int *index_embed, boolean *set_next, int *first_edge)
    /*
      see if the directed edge [v, w] is multiple: if so embed it
      in embedding

      moreover if there are any [v, v] loops do that too
    */
{
    /*
      we take care of multiple edges: for tree edges and back
      edges their multiplicity is indicated by the
      embed_graph[cur_e].mult field (which records the number
      of undirected edges)
      
      for loops hovewer this information is stored in the mult
      field of the FIRST encountered neighbour v in v's neighbour
      list
    */
    t_dlcl      *p;
    int         v_w_in_embed, v_w_prev;
    boolean     do_twins, start, do_first_edge;

    IF_DEB_EMBED_MULT(
           fprintf(stdout, "in recover emb. mult, v %d w %d mult %d\n",
                   v, w, mult);
           )

    /*
      the current index_embed value is the edge [v, w]:
      I must record this value as it will be needed
      later
    */
    v_w_in_embed = *index_embed;
    start = TRUE;
    *set_next = TRUE;
    do_twins = FALSE;
    *first_edge = NIL;
    do_first_edge = FALSE;
    v_w_prev = NIL;
    while (mult > 0)
    {
        ASSERT(!embedg_dlcl_is_empty(mult_edges[v]));
        p = embedg_dlcl_find(mult_edges[v], w);
        /*
          note that using embedg_dlcl_find to always find
          the first in the list with p->info == w
          is ok here since any previous such records would
          have been deleted/removed from the list
        */
        ASSERT(p != NP);
        /*
          otherwise we couldn't have mult > 0 !
        */
        
        *index_embed += 1;
        
        /*
          once again I must use a similar sort of trick as in the
          main function to deal with the inverse edge:
          
          the inverse edge is to be found in mult_edges[w]:
          if p->twin_in_adjl (which was initialised to NIL
          and has NOT been set in the DFS preprocessing),
          if p->twin_in_adjl != NIL, then
          a. its inverse in mult_edges[w] has already been embedded
          in *embedding
          b. its index there is stored in p->twin_in_adjl
          precisely
         */
        if (p->twin_in_adjl != NIL)
        {
            if (! start)
                /*
                  if the first the multiple edges' inverse is already
                  stored, then this is true for ALL of them
                */
            {
                ASSERT(do_twins == TRUE);
            }
            do_twins = TRUE;
        }
        else
            /*
              similarly, if the first the multiple edges' inverse is
              not already stored, then this is true for ALL of them
            */
        {
            ASSERT(do_twins == FALSE);
        }

        embedding[*index_embed].in_adjl = p->in_adjl;
        embedding[*index_embed].mark = NIL;  

        /*
          as we will see do_twins has to be treated differently
        */
        if (!do_twins)
            /*
              this is pretty standard as works as the
              main recover function
            */
        {
            t_dlcl      *i_m_l, *i_p;
            
            embedding[*index_embed].next = *index_embed + 1;
            
            /*
              we store the current index in the embedding in
              the twin/inverse's twin_in_adjl field
            */
            i_p = i_m_l = mult_edges[w];
            ASSERT(!embedg_dlcl_is_empty(i_m_l));
            i_p = embedg_dlcl_find_with_NIL_twin_in_adjl(i_m_l, v);
            ASSERT(i_p != NP);
            ASSERT(i_p->twin_in_adjl == NIL);
            
            i_p->twin_in_adjl = *index_embed;
            
             /*
              to finish off this bit we set embedding[*index_embed + 1].prev
              
              but I can only set this prev field if I haven't reached
              the end of the embedding[] array: this is why we needed
              nbr_e (total number of edges to embed) as input
             */
            
            if (*index_embed < 2 * nbr_e - 1)
            {
                embedding[*index_embed + 1].prev = *index_embed;
            }
        }
        else
            /*
              how to insert the inverses of multiple edges already
              in the embedding:

              if one studies how the twin_in_adjl field has been
              set while dealing with the inverses of the
              present multiple edges one sees that
              the latter must be inserted in counter clockwise
              order (assuming that the inverses were inserted
              in clockwise order)

              this is necessariy to ensure a correct matching between
              the edge and its inverse
            */
        {
            
            embedding[*index_embed].inv = p->twin_in_adjl;
            
            /*
              fix the twin by the same token
            */
            embedding[p->twin_in_adjl].inv = *index_embed;

            /*
              general (reverse) insertion for these edges
            */
            embedding[*index_embed].prev = *index_embed + 1;
            embedding[*index_embed].next = *index_embed - 1;
 
            /*
              ok, that was the easy bit, things are a bit more complicated
              below...
            */
            if (start)
                /*
                  the edges are "wedged" between
                  embedding[v_w_in_embed].prev and v_w_in_embed,

                  hence the following
                */
            {
                v_w_prev = embedding[v_w_in_embed].prev;
                if (v_w_prev == v_w_in_embed)
                    /*
                      in this case the first edge in the adj. list
                      of the vertex whose first_edges is v_w_in_embed
                      will be changed
                      (because we insert in reverse order)
                    */
                {
                    do_first_edge = TRUE;
                }

                embedding[*index_embed].next = v_w_in_embed;
                embedding[v_w_in_embed].prev = *index_embed;
                
                ASSERT(embedding[embedding[*index_embed].inv].prev ==
                       embedding[v_w_in_embed].inv);
                ASSERT(embedding[embedding[v_w_in_embed].inv].next ==
                       embedding[*index_embed].inv);
            }
 
            if (mult == 1)
                /*
                  last inv. edge in this list to add
                */
            {
                ASSERT(v_w_prev != NIL);

                /*
                  must fix embedding[v_w_prev].next appropriately
                  (and embedding[*index_embed].prev)

                  this may be overwritten later on, but not necessarily so

                  the next_set flag will enable us to decide
                  which edge ends this adjacency list: see above
                */
                
                embedding[*index_embed].prev = v_w_prev;
                embedding[v_w_prev].next = *index_embed;
                *set_next = FALSE;

                ASSERT(embedding[embedding[*index_embed].inv].prev ==
                       embedding[*index_embed - 1].inv);
                ASSERT(embedding[embedding[*index_embed - 1].inv].next ==
                       embedding[*index_embed].inv);

                if (do_first_edge)
                    /*
                      the first edge is the last one added
                    */
                {
                    *first_edge = *index_embed;
                }

                embedding[v_w_in_embed].next = *index_embed + 1;
                if (*index_embed < 2 * nbr_e - 1)
                {
                   embedding[*index_embed + 1].prev = v_w_in_embed;
                }
            }

            ASSERT(embedding[embedding[*index_embed].inv].prev ==
                   embedding[embedding[*index_embed].next].inv);
        }
        
        /*
          to finish off this bit we delete the p record from m_l
          and set embedding[*index_embed + 1].prev
        */
        mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);

        mult--;
        start = FALSE;
    }
    /*
      conclusion: sevral days to get this working! *sigh*
    */
}




static void 
embedg_recover_embedding_embed_loops (t_dlcl **mult_edges,
        t_embed_sparse_rep *embedding, int nbr_e, int v,
        int *index_embed, boolean *set_next)
    /*
      embed the [v, v] loops 
    */
{
    /*
      the loops' multiplicity is stored in the mult
      field of the FIRST encountered neighbour v in v's neighbour
      list
    */
    t_dlcl      *p;
    int         nbr_loops;
    
    /*
      have a look if there are any [v. v] loops
    */
    p = embedg_dlcl_find(mult_edges[v], v);
    if (p == NP)
    {
        return;
    }

    /*
      when there are loops to add to the adjaceny list,
      edge insertion resume in the "normal" clockwaise saya, way:
      so we reset set_next to true
    */
    *set_next = TRUE;
    
    nbr_loops = p->mult;
    ASSERT(nbr_loops % 2 == 0);
    /*
      we counted directed edges
    */
    nbr_loops /= 2;

    IF_DEB_EMBED_LOOPS(
           fprintf(stdout, "in recover emb. loops, nbr_loops [v, v] %d\n",
                   nbr_loops);
           )
        
    while (nbr_loops > 0)
        /*
          a loop requires to embed two directed edges
        */
    {
        p = embedg_dlcl_find(mult_edges[v], v);
        ASSERT(p != NP);

        *index_embed += 1;
        
        embedding[*index_embed].in_adjl = p->in_adjl;
        embedding[*index_embed].next = *index_embed + 1; 
        embedding[*index_embed].mark = NIL; 
        embedding[*index_embed].inv = *index_embed + 1;
        embedding[*index_embed + 1].prev = *index_embed;
        
        mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
        
        IF_DEB_EMBED_LOOPS(
           fprintf(stdout, "in recover emb. loops, mid\n");
           embedg_dlcl_print(mult_edges[v]);
           );

        /*
          now do the "inverse" loop
        */
        p = embedg_dlcl_find(mult_edges[v], v);
        ASSERT(p != NP);
        
        *index_embed += 1;
        
        embedding[*index_embed].in_adjl = p->in_adjl;
        embedding[*index_embed].next = *index_embed + 1; 
        embedding[*index_embed].mark = NIL;
        embedding[*index_embed].inv = *index_embed - 1;

        if (*index_embed < 2 * nbr_e - 1)
        {
            embedding[*index_embed + 1].prev = *index_embed;
        }
        mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
        
        nbr_loops--;

        IF_DEB_EMBED_LOOPS(
           fprintf(stdout, "in recover emb. loops, end\n");
           embedg_dlcl_print(mult_edges[v]);
           );
    }
}




void 
embedg_recov_embed_walk_proper_face (int n, int e, t_adjl_sparse_rep *A,
        t_embed_sparse_rep *embedding, boolean MARK, int mark)
    /*
      do a proper face walk in the recovered embedding starting
      at index e in the embedding
    */
{
    int          cur, next;
    
    IF_DEB_FACES(
                 fprintf(stdout, "recov. emb. proper face walk\n");
                 fprintf(stdout, "[-, %d] ",
                         A[embedding[e].in_adjl].end_vertex);
                 )
        
    cur = e;
    next = NIL;
    while (next != e)
        /*
          to get the next in a proper face traversal:
          get the previous of the cur's inverse
        */
    {
        int     inv;

        inv = embedding[cur].inv;
        next = embedding[inv].prev;

        ASSERT(embedding[next].mark != mark);
        
        if (MARK)
        {
            embedding[next].mark = mark;
        }
        
        cur = next;
        IF_DEB_FACES(
                     fprintf(stdout, "[-, %d] ",
                             A[embedding[cur].in_adjl].end_vertex);
                     )
    }
    IF_DEB_FACES(
                 fprintf(stdout, "\n");
                 )
}
      


boolean 
embedg_check_recov_embedding (int n, int nbr_e, int nbr_comp,
        t_ver_sparse_rep *vertices, t_adjl_sparse_rep *A,
        t_embed_sparse_rep *embedding)
    /*
      check if the recovered embedding is a valid embedding
      SHOULD ONLY be use after creation, that is, after having
      recovered the embedding from the VES structure
      (because of the mark MIN_EMBED_MARK we use)
    */
{
    int          v, e, f;

    f = 0;
    /*
      do all the edges in embedding:
      careful: we have 2 * nbr_e to visit (the edge and its inverse!)
    */
    for (e = 0; e < 2 * nbr_e; e++)
    {
        /*
          we check if the current edge is marked: if not, we
          traverse a proper face bordered by this edge
        */
        if (embedding[e].mark != MIN_EMBED_MARK)
            /*
              we --hopefully-- perform this check only after creation
              where mark == NIL
            */
        {
            embedg_recov_embed_walk_proper_face(n, e, A, embedding,
                                                TRUE, MIN_EMBED_MARK);
            f++;
        }
    }

    /*
      must also count a face for each isolated vertex
    */
    for (v = 0; v < n; v++)
    {
        if (vertices[v].first_edge == NIL)
            f++;
    }
    
    IF_DEB_CHECK_EMBED(
                       fprintf(stdout, "recovered embedding, n: %d\t e: %d\t C: %d\t f: %d\n",
                               n, nbr_e, nbr_comp, f);
                       )
        
    return f == 2 * nbr_comp + nbr_e - n ? TRUE : FALSE;
}


t_dlcl **
embedg_recover_obstruction (t_ver_edge *embed_graph, int n, minor m, int *nbr_e)
    /*
      recover the obstruction as a t_dlcl * structure:
      and return the number of edges: lets say we agree on returning
      the number of undirected edges
      -- I don't know yet which way to do, directed or undirected???

      so far in the algorithm we only dealt with DFIs,
      but now, we retrieve the obstruction not wrt DFIs but
      wrt the vertices' labels
    */
{
    /*
      so I am looking, in embed_graph, for the vertices and edges
      marked MARK_MINORS(n)
    */

    int          v;
    t_dlcl       **obs;

    obs = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
    for (v = 0; v < n; v++)
        obs[v] = NP;

    *nbr_e = 0;
    for (v = 0; v < 2*n; v++)
        /*
          must check real vertices as well as virtual vertices
        */
    {
        int      e;

        if (embed_graph[v].link[0] == v)
            /*
              isolated vertex case
            */
        {
            ASSERT(embed_graph[v].link[1] == v);
            continue;
        }

        e = embed_graph[v].link[0];
        while (e != v)
        {
            ASSERT(embedg_VES_is_edge(n, e));
            if (embed_graph[e].visited == MARK_MINORS(n))
            {
                int        cur_v, neigh;

                /*
                  virtual vertices may still hang around
                */
                /*
                  let's get the "actual" v:
                  note that the statement below is safe since if v were
                  not a valid virtual vertex (ie [v - n].DFS_parent = n)
                  it would have an empty
                  adjacency list and we wouldn't be there anyway
                */
                cur_v = embedg_VES_get_ver(embed_graph, n, v);
        
                neigh = embedg_VES_get_ver(embed_graph, n,
                                           embed_graph[e].neighbour);

                /*
                  again, cur_v and neigh are DFIs,
                  we want vertex labels at this stage
                */
                cur_v = embed_graph[cur_v].label;
                neigh = embed_graph[neigh].label;
                sparseg_dlcl_append_to_neigh_list(obs, n, cur_v, neigh,
                                                  embed_graph[e].in_adjl);
                (*nbr_e)++;
            }
            e = embed_graph[e].link[0];
        }
    }
    
    IF_DEB_OBS(
               fprintf(stdout, "recovering the obstruction\n");
               sparseg_dlcl_print(obs, n);
    );

    ASSERT(*nbr_e % 2 == 0);
    *nbr_e /= 2;
    
    return obs;
}


static t_dlcl **
embedg_get_reduced_obs (t_dlcl **obs, int n)
    /*
      reduce the obstruction by removing all degree 2 vertices
      (so that they become isolated vertices)
    */
{
    t_dlcl       **reduced;
    int          v;

    reduced =  (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
    for (v = 0; v < n; v++)
    {
        reduced[v] = embedg_dlcl_copy(obs[v]);
    }
        
    for (v = 0; v < n; v++)
    {
        t_dlcl   *n_l, *n_l_b, *p, *new_n_v, *n_l_x, *b_in_n_x;
        int      a, b, n_x;

        n_l = reduced[v];
        while (!embedg_dlcl_is_empty(n_l)
                && embedg_dlcl_list_last(n_l) == embedg_dlcl_list_next(n_l))
            /*
              pick out which  vertices have deg 2
            */
        {
            a = n_l->info;
            b = embedg_dlcl_list_next(n_l)->info;
            /*
              we remove the edge (v, b), or rather, we identify v and b:
              b will then be an isolated vertex
              
              fix v's neighbour list: all of b's neighbours
              are now v's neighbours
            */
            reduced[v] = n_l =
                embedg_dlcl_delete_rec(n_l, embedg_dlcl_list_last(n_l));
            
            p = n_l_b = reduced[b];
            ASSERT(!embedg_dlcl_is_empty(n_l_b));
            n_x = p->info;
            if (n_x != v)
            {
                new_n_v = embedg_dlcl_rec_new(n_x);
                reduced[v] = n_l = embedg_dlcl_cat(n_l, new_n_v);
                
                /*
                  and in n_x neighbour list, we must replace b by v
                */
                n_l_x = reduced[n_x];
                b_in_n_x = embedg_dlcl_find(n_l_x, b);
                b_in_n_x->info = v;
            }
            /*
              and do this for all of b's neighbours
            */
            p = embedg_dlcl_list_next(p);
            while (p != n_l_b)
            {
                n_x = p->info;
                if (n_x != v)
                {
                    new_n_v = embedg_dlcl_rec_new(n_x);
                    reduced[v] = n_l = embedg_dlcl_cat(n_l, new_n_v);
                    n_l_x = reduced[n_x];
                    b_in_n_x = embedg_dlcl_find(n_l_x, b);
                    b_in_n_x->info = v;
                }
                p = embedg_dlcl_list_next(p);
            }
            embedg_dlcl_delete(reduced[b]);
            reduced[b] = NP;
        }
    }

    IF_DEB_CHECK_OBS(
                     fprintf(stdout, "reducing the obstruction\n");
                     sparseg_dlcl_print(reduced, n);
                     )

    /*
      now check no degree 2 vertices are left
    */
    for (v = 0; v < n; v++)
    {
        t_dlcl   *n_l;
        
        n_l = reduced[v];
        if (!embedg_dlcl_is_empty(n_l))
        {
            ASSERT(embedg_dlcl_list_last(n_l) != embedg_dlcl_list_next(n_l));
        }
    }

    return reduced;
}

static boolean 
embedg_is_red_obs_K33 (t_dlcl **reduced, int n)
    /*
      check if the (reduced) obstruction is indeed K33 
    */
{
    int          v, order, vs[6], i, b1[3];
    
    /*
      check that order == 6 and that the obstruction is cubic
    */
    order = 0;
    for (v = 0; v < n; v++)
    {
        if (!embedg_dlcl_is_empty(reduced[v]))
        {
            if (order == 6)
            {
                return FALSE;
            }
            order++;
            vs[order - 1] = v;
            
            if (embedg_dlcl_length(reduced[v]) != 3)
            {
                return FALSE;
            }
        }
    }
    if (order != 6)
    {
        return FALSE;
    }
    
    /*
      check if bipartite
    */
    v = vs[0];
    ASSERT(!embedg_dlcl_is_empty(reduced[v]));
    b1[0] = reduced[v]->info;
    b1[1] = embedg_dlcl_list_next(reduced[v])->info;
    b1[2] = embedg_dlcl_list_prev(reduced[v])->info;
    
    for (i = 1; i < 6; i++)
    {
        t_dlcl      *n_v;

        v = vs[i];
        n_v = reduced[v];
        ASSERT(!embedg_dlcl_is_empty(n_v));
        if (n_v->info == b1[0]
            || embedg_dlcl_list_next(n_v)->info == b1[0]
            || embedg_dlcl_list_prev(n_v)->info == b1[0])
        {
            if ((n_v->info != b1[1]
                 && embedg_dlcl_list_next(n_v)->info != b1[1]
                 && embedg_dlcl_list_prev(n_v)->info != b1[1])
                &&
                (n_v->info != b1[2]
                 && embedg_dlcl_list_next(n_v)->info != b1[2]
                 && embedg_dlcl_list_prev(n_v)->info != b1[2]))
            {
                return FALSE;
            }
        }
        else
        {
            if ((n_v->info == b1[1]
                 || embedg_dlcl_list_next(n_v)->info == b1[1]
                 || embedg_dlcl_list_prev(n_v)->info == b1[1])
                ||
                (n_v->info == b1[2]
                 || embedg_dlcl_list_next(n_v)->info == b1[2]
                 || embedg_dlcl_list_prev(n_v)->info == b1[2]))
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}


static boolean 
embedg_is_red_obs_K5 (t_dlcl **reduced, int n)
    /*
      check if the (reduced) obstruction is indeed K5
    */
{
    int          v, order;
    
    /*
      check that order == 5 and that the obstruction is quadric
    */
    order = 0;
    for (v = 0; v < n; v++)
    {
        if (!embedg_dlcl_is_empty(reduced[v]))
        {
            if (order == 5)
            {
                return FALSE;
            }
            order++;
            
            if (embedg_dlcl_length(reduced[v]) != 4)
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}


boolean 
embedg_check_recov_obs (t_dlcl **obs, int n, minor m)
    /*
      check if the recovered obstruction is one of K33 or K5
    */
{
    t_dlcl      **reduced;
    boolean     ans;

    reduced = embedg_get_reduced_obs(obs, n);
    if (m != MINOR_E5)
    {
        ans = embedg_is_red_obs_K33(reduced, n);
    }
    else
    {
        ans = embedg_is_red_obs_K5(reduced, n);
    }

    sparseg_dlcl_delete(reduced, n);
    return ans;
}
/*
 *  obstruction.c
 */
 
/*
  What:
  *****
  
  Implementing:

  The graph is not planar: we recover the obstruction from the VES structure
  and check it as well.
  (Some of these checks will disappear later)
  


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
#define IF_DEB_OBS(x) {}
#define IF_DEB_CHECK_OBS(x) {}
#define IF_CPU(x) {}
#define IF_DEB_MINOR(x) {}
 
 
/* aproto: header embed_graph_protos.h */

void 
embedg_obstruction (
    t_ver_sparse_rep *V,
    t_adjl_sparse_rep *A,       /* the input graph as a sparse graph */
    t_dlcl **dfs_tree,      /* a sparse graph rep. for the dfs tree
                                      -- vertices are as DFIs
                                      -- and children are ordered wrt
                                      lowpoint value
                                  */
    t_dlcl **back_edges,    /* for each vertex v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v
                                      (vertices are given as DFIs)
                                  */
    t_ver_edge *embed_graph,    /* output of tester */
    int n,               /* size of the graph */
    int *edge_pos,       /* pos. in embed_graph for addition
                                     of the next edge */
    int v,
    int w_in,         /* the unembedded directed back edge
                                     [w_in, v]
                                   */
    t_ver_sparse_rep **OV,      /* the obstruction as an adjacency list */
    t_adjl_sparse_rep **OA,
    int *nbr_e_obs      /* obstruction's #edges */
)
                                    
    /*
      the graph is non planar: we must mark & recover the K33 or K5
      homeomorph
    */
{
    int          *ver_orient;
    minor        m;
    t_dlcl       **obs;
    
    /*
      this is magma code - must be removed
    */
    float      sttime, time_to_now;
 
 IF_CPU(
    sttime = time_current_user();
       )

    /*
      we will NOT remove the short-cut edges at this stage:
      we'll have to perform another walkdown in embedg_iso_is_minor_A
      so
      1. saves time when looking for ext. active vertices
      2. more importantly this enables us to ascertain that the number of
         edges in embed_graph (even after completing whichever obstruction
         applying in this case) will NEVER be > 3*n - 5!!!
      3. SCEs are then removed in embedg_iso_is_minor_A
         (obligatory path for every possible case)
    */

    /*
      we must compute each vertex's orientation (wrt flipped bicomps)
      and set the edges' orientation:
      
      the other day I was wondering why this was necessary in this
      instance (because after all we won't get an embedding):
      orientation is required bacause later in the piece we
      do a proper face traversal (I guess for Minor C testing)
    */
    ver_orient = embedg_vertices_orientation(embed_graph, n);
    embedg_VES_set_orientation(embed_graph, n, ver_orient);
    mem_free(ver_orient);

    m = embedg_mark_obstruction(dfs_tree, back_edges,
                                embed_graph, n, edge_pos, v, w_in);

    /*
      get the obstruction
    */
    obs = embedg_recover_obstruction(embed_graph, n, m, nbr_e_obs);

    /*
      and check it
    */
    if (!embedg_check_recov_obs(obs, n, m))
    {
        sparseg_dlcl_delete(obs, n);
        DIE();
    }
    
    sparseg_dlcl_to_sparseg(obs, n, *nbr_e_obs, OV, OA);
    sparseg_dlcl_delete(obs, n);

    /*
      just for the sake of it, chcek if the obstruction is
      a subgraph of the input graph
    */
    if (!sparseg_adjl_sub(*OV, n, *OA, V, n, A))
    {
        DIE();
    }
    
    IF_DEB_OBS(
               sparseg_adjl_print(*V, n, *A, FALSE);
               )
    
    IF_CPU(
           fprintf(stdout, "CPU for obstruction recovering %f\n",
                   (time_current_user() - sttime));
           )
}







minor 
embedg_mark_obstruction (
    t_dlcl **dfs_tree,      /* a sparse graph rep. for the dfs tree
                                      -- vertices are as DFIs
                                      -- and children are ordered wrt
                                      lowpoint value
                                  */
    t_dlcl **back_edges,    /* for each vertex v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v
                                      (vertices are given as DFIs)
                                  */
    t_ver_edge *embed_graph,    /* output of tester */
    int n,               /* size of the graph */
    int *edge_pos,       /* pos. in embed_graph for addition
                                     of the next edge */
    int v,
    int w_in         /* the unembedded directed back edge
                                     [w_in, v]
                                   */
)
    /*
      the graph is non planar: we must mark & recover the K33 or K5
      homeomorph
    */
{
    int          c, vr, x, y, w;
    int          *path_v, *path_e, nbr_v, entry_in_path_e;
    boolean      px_attached_high, py_attached_high, is_minor_D;
    minor        m;
    
 
  IF_CPU(
    float      sttime; float time_to_now;

    sttime = time_current_user();
        )
    

    /*
      find c such that v^c is the root of the biconnected
      component on which the walkdown failed
    */
    c = embedg_iso_get_c_of_v(embed_graph, n, v, w_in);

    /*
      now: decide which minor we are dealing with and mark the
      appropriate one (vertices/edges marked as MARK_MINOR(n)
      in embed_graph)
    */
    if (embedg_iso_is_minor_A(embed_graph, n, edge_pos, v, c, &vr))
    {
        embedg_mark_minor_A(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, c, vr);
        
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor A\n");
                     )

        return MINOR_A;
    }

    /*
      get the externally active vertices x & y and the pertinent w
      on the external face of the bicomp rooted by v^c

      and determine if minor B
    */
    if (embedg_iso_is_minor_B(embed_graph, n, edge_pos, v, c,
                                   &x, &y, &w))
    {
        embedg_mark_minor_B(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, c,
                            x, y, w);
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor B\n");
                     )

        IF_CPU(
               fprintf(stdout, "CPU for obstruction isolation %f\n",
                       time_current_user() - sttime);
               )
            
        return MINOR_B;
    }

    /*
      the remaining cases: must get the highest x-y path

      it will be containing in path_v (vertices), path_e (edges)
    */
    embedg_iso_get_highest_x_y_path(embed_graph, n, MARK_EXT_FACE(n),
                                    MARK_EXT_FACE_L(n),
                                    MARK_EXT_FACE_R(n),
                                    v, c, x, y, w,
                                    &path_v, &path_e,
                                    &nbr_v, &entry_in_path_e,
                                    &px_attached_high,
                                    &py_attached_high,
                                    &is_minor_D);

    /*
      we are in the minor C case if either one of p_x or p_y
      is attached high
    */
    if (px_attached_high || py_attached_high)
    {
        embedg_mark_minor_C(dfs_tree, back_edges, embed_graph, n, edge_pos,
                            v, c, x, y, w,
                            path_v, path_e, nbr_v,
                            px_attached_high, py_attached_high);
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor C\n");
                     )

        mem_free(path_v);
        mem_free(path_e);
        
        IF_CPU(
               fprintf(stdout, "CPU for obstruction isolation %f\n",
                       time_current_user() - sttime);
               )
            
        return MINOR_C;
    }

    if (is_minor_D)
    {
        embedg_mark_minor_D(dfs_tree, back_edges, embed_graph, n, edge_pos,
                            v, c, x, y, w,
                            path_v, path_e, nbr_v, entry_in_path_e);
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor D\n");
                     )

        mem_free(path_v);
        mem_free(path_e);
        
        IF_CPU(
               fprintf(stdout, "CPU for obstruction isolation %f\n",
                       time_current_user() - sttime);
               )
            
        return MINOR_D;
    }

    /*
      finally, the minor E case
    */
    m = embedg_mark_minor_E(dfs_tree, back_edges, embed_graph, n, edge_pos,
                            v, c, x, y, w,
                            path_v, path_e, nbr_v);
    switch (m)
    {
    case MINOR_E1:
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor E1\n");
                     )
        break;
    case MINOR_E2:
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor E2\n");
                     )
        break;
    case MINOR_E3:
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor E3\n");
                     )
        break;
    case MINOR_E4:
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor E4\n");
                     )
        break;
    case MINOR_E5:
        IF_DEB_MINOR(
                     fprintf(stdout, "Minor E5\n");
                     )
        break;
    case MINOR_A: 
    case MINOR_B: 
    case MINOR_C: 
    case MINOR_D:
    case MINOR_E:
    case NBR_MINORS:
        break;
    }

    mem_free(path_v);
    mem_free(path_e);
    
    IF_CPU(
           fprintf(stdout, "CPU (scaled) for obstruction isolation %f\n",
                   (time_current_user() - sttime) / e);
           )
        
    return m;
}
/*
 *  isolator.c
 */
 
/*
  What:
  *****
  
  Implementing:

  The graph is non planar: we isolate the obstruction.
  
 
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 
 
  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca
 
 
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********
 
  Paulette Lieby (Magma), Brendan McKay (ANU)
 
  Started October 2001
*/
 
 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
#define IF_DEB_TREE(x)    {}
#define IF_DEB_EDGES(x) {}
#define IF_CPU(x) {}
/* #define IF_DEB_MINOR(x) {x}  -- Not Used  */
 
 
/* aproto: header embed_graph_protos.h */
 
#ifndef PLANAR_IN_MAGMA
#endif
 



int 
embedg_iso_get_c_of_v (t_ver_edge *embed_graph, int n, int v, int w)
    /*
      the edge [v, w] (w a descendant of v) remains unembedded
      after the walkdown returns

      find c such that v^c is the root of the biconnected
      component on which the walkdown failed
    */
{
    /*
      how to do this??? easy! follow the DFS tree path as given
      by the field DFS_parent
    */

    int           u;

    u = embed_graph[w].DFS_parent;
    while (embed_graph[u].DFS_parent != v)
    {
        u = embed_graph[u].DFS_parent;
    }
    /*
      this is guaranteed to succeed given the structure of the DFS tree
      and the fact that there exists a  back edge [w, v]
    */
    
    return u;
}


boolean 
embedg_iso_is_minor_A (t_ver_edge *embed_graph, int n,
        int *edge_pos, int v, int c, int *vr)
    /*
      determines if the obstruction is a minor A
    */
{
    /*
      to do this we again call the walkdown routine with v^c as input,
      the walkdown routine will fail (since there will be an
      un-embedded back edge incident to v and to a vertex
      in the subtree rooted by v^c)

      the obstruction is a minor A if the merge queue returned by the
      walkdown is non-empty, if this is the case we return
      the bicomp last appended to the queue
    */
    int             vv;
    t_merge_queue   q;

    vv = c + n;
    
    q = embedg_walkdown(embed_graph, n, edge_pos, vv);
    /*
      we MUST remove the SCEs here: this is the only place where it
      will be done when looking for and recovering an obstruction

      this is safe since this very function applies to ALL cases!
    */
    embedg_remove_SCE(embed_graph, n, *edge_pos);

    if (!embedg_merge_queue_empty(q))
        /*
          the bicomp of interest is the last in the queue
        */
    {
        int         r, rin, vrout;
        
        embedg_merge_queue_prune(&q, &r, &rin, vr, &vrout);
        embedg_merge_queue_delete(q);
        return TRUE;
    }
    else
    {
        embedg_merge_queue_delete(q);
        return FALSE;
    }
}


void 
embedg_iso_get_x_y_w (t_ver_edge *embed_graph, int n, int v, int r,
        int c, int mark, int mark_l, int mark_r, int *x, int *y, int *w)
    /*
      the obstruction is one of minor B, C, D, E.
      
      get the externally active vertices x & y along the
      external face  paths starting at r^c

      get a pertinent vertex w along the lower external
      face path between x and y

      external activity and pertinence are wrt v
      
      all the vertices on the external face r^c...x...w
      and r^c...y...w will be marked (the visited field)
    */
{
    int          vr, vrin, x_y[4];
    int          s, sin, cur, curin;

    vr = c + n;

    /*
      find x and y first:

      note that we mark the vertices on the external face r^c...x
      and r^c...y

      more on that below
    */
    embed_graph[vr].visited = mark;
    for (vrin = 0; vrin <= 1; vrin++)
    {
        int      m;

        m = vrin == 0 ? mark_l : mark_r;
        embedg_VES_get_succ_ext_active_on_ext_face(embed_graph, n, v,
                                                        vr, vrin,
                                                        TRUE, m,
                                                        &s, &sin);
        x_y[vrin] = s;
        x_y[vrin + 2] = sin;
        /*
          note the bizarre way I store the active vertex
          and the direction out of which to continue a walk
          on the lower external face as described above
        */
    }
    *x = x_y[0];
    *y = x_y[1];
    
    /*
      next get the pertinent w on the lower external face from x to y
    */
    cur = x_y[0];
    curin = x_y[2];
    embedg_VES_get_succ_pertinent_on_ext_face(embed_graph, n, v,
                                                   cur, curin,
                                                   TRUE, mark_l, w, &sin);

    /*
      now all the vertices  r^c...x...w and r^c...y have been marked,
      it remains to mark the vertices on the y...w external face path

      (will need to be able to distinguish the external face  later on)

      Note the way the external face is marked (needed when recovering
      the highest x-y path):
      mark_l for the path v^c...x...w
      mark_r for the path v^c...y
      mark for the lower external face y...w
    */
    cur = x_y[1];
    curin = x_y[3];
    s = n;
    while (s != *w)
    {
        embedg_VES_get_succ_pertinent_on_ext_face(embed_graph, n, v,
                                                       cur, curin,
                                                       TRUE, mark, &s, &sin);
        cur = s;
        curin = sin;
    }

    IF_DEB(
           fprintf(stdout, "get x, y & w: the external face\n");
           fprintf(stdout, "%d\t", vr);
           cur = vr;
           curin = 0;
           while (s != vr)
           {
               embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                                    cur, curin,
                                                    FALSE, 0, &s, &sin);
               cur = s;
               curin = sin;
               fprintf(stdout, "%d\t", s);
           }
           fprintf(stdout, "\n");
           )
}




boolean 
embedg_iso_is_minor_B (t_ver_edge *embed_graph, int n, int *edge_pos,
        int v, int c, int *x, int *y, int *w)
    /*
      determines if the obstruction is a minor B and return x, y
      (ext. active) and w (pertinent)
    */
{
    /*
      get x & y the ext. active vertices on the (external face)
      path out of v^c,
      and w the pertinent vertex on the lower external face x-y

      PLUS mark the whole external face with MARK_EXT_FACE(n)
    */
    embedg_iso_get_x_y_w(embed_graph, n, v, v, c,
                              MARK_EXT_FACE(n),
                              MARK_EXT_FACE_L(n), MARK_EXT_FACE_R(n),
                              x, y, w);

    if (embedg_dlcl_is_empty(embed_graph[*w].pertinent_bicomp_list))
        /*
          w has no pertinent child bicomp: not a minor B
        */
        return FALSE;
    else
    {
        t_dlcl      *pert_l;
        int         l;

        pert_l = embed_graph[*w].pertinent_bicomp_list;
        l = embedg_dlcl_list_last(pert_l)->info;
        /*
          if w has an ext. active pertinent child bicomp then minor B

          note that we need to know if w has an ext. active AND pertinent
          bicomp child: so it is NOT good enough to test
          w's separated_DFS_child_list as is done in
          embedg_VES_is_ver_ext_active!!!!!!!!!

          PLUS: l is actually a VIRTUAL vertex: to check its lowpoint
          I must take its DFS child l - n !!!!!!!!
        */
        ASSERT(embedg_VES_is_virtual_vertex(n, l));
        l = l - n;
        return embed_graph[l].lowpoint < v ? TRUE : FALSE;
    }
}

void 
embedg_iso_get_highest_x_y_path (
    t_ver_edge *embed_graph,
    int n,
    int mark,
    int mark_l,
    int mark_r,
    int v,
    int c,
    int x,
    int y,
    int w,
    int **path_v,     /* stack of vertices in x-y path */
    int **path_e,     /* stack of egdes in x-y path */
    int *nbr_v,         /* number of vertices in path_v */
    int *entry_in_path_e, /* the in direction for the FIRST edge in
                                      path_e: needed later on *sigh*
                                   */
    boolean *px_attached_high,
    boolean *py_attached_high,
    boolean *is_minor_D
)
    /*
      the obstruction is one of minor C, D, E.

      we want to recover the highest x-y path:
      the obstructing path attached to the external faces v^c - x - w
      and v^c - y - w

      while doing all this we also determine if the case is a minor C
      or a minor D
    */
{
    /*
      the path is obtained by walking the proper face starting at v
      where ALL the edges incident to v^c BUT the ones bordering
      the external face have been removed

      I won't I don't think remove these edges, but instead I'll be
      implementing an "avoidance" walk
    */

    int          vv, s, sin, p_x, p_y, cur_v, cur_vin;
    int          e, ein, s_e, s_ein;
    boolean      avoid_vv;
    
    /*
      must start the walk at edge embed_graph[v^c].link[1 ^ 0],
      (vvin = 0 is in direction of x, see embedg_iso_get_x_y_w)
    */
    vv = c + n;
    e = embed_graph[vv].link[1];
    ein = 0;     /* because of adjacency list consistency */

    *path_v = (int *) mem_malloc(sizeof(int) * n);
    *path_e = (int *) mem_malloc(sizeof(int) * n);
    (*nbr_v) = -1;
    
    /*
      recall that in embedg_iso_get_x_y_w we did mark
      (with mark, mark_l, mark_r)
      ALL the vertices lying on the external face walk starting
      & ending at v^c: we will use this fact to enable us
      to decide if a vertex is on the external face
      (as opposed to being on the internal face)
    */

    s = embed_graph[e].neighbour;
    ASSERT(embed_graph[s].visited == mark_l);
    /*
       this must be the case since s lies on the external face
       starting at v^c in x's direction
      -- we push s onto the stack
    */
    (*path_v)[++(*nbr_v)] = s;

    /*
      start the proper face walk which "avoids" v^c since the
      internal edges incident to v^c are supposed to have
      been removed

      please read on
    */
    avoid_vv = FALSE;
    while (TRUE)
    {
        boolean      av;
        
        av = 
            embedg_VES_get_succ_on_proper_face_with_avoidance(
                                                          embed_graph, n,
                                                          e, ein, vv,
                                                          FALSE, 0,
                                                          &s, &s_e, &s_ein);
        avoid_vv = av == TRUE ? av : avoid_vv;
        if (embed_graph[s].visited == mark_l)
            /*
              means that s is still on the external face:
              empty the path's stack and push s
            */
        {
            (*nbr_v) = -1;
            (*path_v)[++(*nbr_v)] = s;
            e = s_e;
            ein = s_ein;
        }
        else if (*nbr_v == 0)
            /*
              s is the first encountered vertex after
              path_v[0] which does not
              lie on the external face v^c...c...w

              given the way we pushed things on the vertex stack, path_v[0]
              will be the point of attachement of the x-y path
              on the v^c...x...w external face

              path_e[0] will contain nothing: a dummy
              
              path_e[1] will be the first edge in the x-y path
              (and entry_in_path will give the in-direction to this edge)
              
              oh yes!, we break the loop at this point if
              the vertex s lies on the v^c...y...w external face
            */
        {
            ASSERT(embed_graph[(*path_v)[0]].visited == mark_l);
            /*
              the first vertex on the path must be on the
              v^c...x...w external face
            */
            (*path_v)[++(*nbr_v)] = s;
            /*
              and now we also push the edge on the edge stack

              I'll need this later to initiate a proper face walk
              starting at the first vertex/edge in the x-y path,
              which is the same as starting from s_e
            */
            (*path_e)[*nbr_v] = s_e;
            *entry_in_path_e = s_ein;
            e = s_e;
            ein = s_ein;

            /*
              since we are at the start of the path, we must not
              forget to reset avoid_vv
            */
            avoid_vv = FALSE;
            
            if (embed_graph[s].visited == mark_r
                || embed_graph[s].visited == mark)
                /*
                  we have reached the v^c...y...w external face:
                  we can stop here
                */
            {
                break;
            }

            /*
              if not finished yet,
              we also mark s (and path_v[0]) as visited:
              later on we'll need to recognise which of the vertices
              in path have already been encountered
              (in case of encountering a cut-vertex due to the
              "removal" of the "internal" edges incidnet ot v^c)

              note that we mark s as visited iff s if not already
              on the v^c..y..w external face
            */

            ASSERT(embedg_VES_is_vertex(n, (*path_v)[0]));
            ASSERT(embedg_VES_is_vertex(n, s));

            embed_graph[s].visited = MARK_X_Y_PATH(n);
        }
        else  if (embed_graph[s].visited == MARK_X_Y_PATH(n))
            /*
              this means that s is a cut vertex on the internal
              face walk: pop all the vertices from path
              until s's last occurrence in path
            */
        {
            ASSERT((*nbr_v) >= 0);
            while ((*path_v)[(*nbr_v)] != s)
            {
                (*nbr_v)--;
                ASSERT((*nbr_v) >= 0);
                /*
                  note that s should be somewhere in path!
                */
            }
            /*
              note also that popping from path_v also implies
              popping from path_e
            */
            e = s_e;
            ein = s_ein;
        }
        else
            /*
              we push s and s_e on their respective stacks
            */
        {
            (*path_v)[++(*nbr_v)] = s;
            (*path_e)[*nbr_v] = s_e;
            e = s_e;
            ein = s_ein;
            
            if (embed_graph[s].visited == mark_r
                || embed_graph[s].visited == mark)
                /*
                  again, s lies on the v^c...y...w external face:
                  we end the walk: path_v now contains the highest x-y path

                  note that there can be no conflict between
                  mark_r or mark and MARK_X_Y_PATH(n) since
                  we mark with MARK_X_Y_PATH iff the vertex
                  is NOT marked with mark_r/mark!
                */
            {
                break;
            }
            else
                /*
                  we must mark this vertex as MARK_X_Y_PATH since we aren't
                  finished yet
                */
            {
                embed_graph[s].visited = MARK_X_Y_PATH(n);
            }
        }
    }

    /*
      there is only one thing remaining to do: see if p_x or
      p_y are attached high
      (ie closer to v^c than x or y resp.)

      we walk the external face starting at v^c in y's direction
      (again see embedg_iso_get_x_y_w)
    */
    *px_attached_high = TRUE;
    p_x = (*path_v)[0];
    /*
      p_y denotes the attachement point of the x-y path
      on the v^c...y...w external face
    */

    s = n;
    cur_v = vv;
    cur_vin = 0;
    while (s != p_x)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                             cur_v, cur_vin,
                                             FALSE, 0, &s, &sin);
        if (s == x)
        {
            *px_attached_high = FALSE;
            break;
        }
        cur_v = s;
        cur_vin = sin;
    }

    *py_attached_high = TRUE;
    p_y = (*path_v)[*nbr_v];
    /*
      p_y denotes the attachement point of the x-y path
      on the v^c...y...w external face
    */

    s = n;
    cur_v = vv;
    cur_vin = 1;
    while (s != p_y)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                             cur_v, cur_vin,
                                             FALSE, 0, &s, &sin);
        if (s == y)
        {
            *py_attached_high = FALSE;
            break;
        }
        cur_v = s;
        cur_vin = sin;
    }

    /*
      now we are in the minor C case if either p_x or p_y are
      attached high

      the minor D case:
      this happens when there is a path v^c - z where z lies
      on the x-y path

      that is, when
      
      either v^c has been effectively "avoided" within the
      embedg_VES_get_succ_on_proper_face_with_avoidance function
      BUT ONLY if this "avoidance" happened AFTER having
      encountered the very first vertex on the x-y path!

      or when a cut vertex has been encountered on the x-y path:
      separable components on this walk can only occur
      if one walks the face while skipping the edges incident to v^c

      in any case this means that checking the return from 
      the embedg_VES_get_succ_on_proper_face_with_avoidance function
      is enough: this is the purpose of avoid_vv.
    */

    *is_minor_D = !(*px_attached_high || *py_attached_high) && avoid_vv;


    IF_DEB(
           int    i;
           
           fprintf(stdout, "x-y path\t");
           for (i = 0; i <= *nbr_v; i++)
               fprintf(stdout, "%d\t", (*path_v)[i]);
           fprintf(stdout, "\n");
           )
}


/*
 *  embedg_misc.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Some high level routinse for the VES structure.
  See VES_misc.c.


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
#define IF_DEB_TREE(x)    {}
 
 
 
/* aproto: header embed_graph_protos.h */
 
#ifndef PLANAR_IN_MAGMA
#endif
 



void 
embedg_VES_delete (t_ver_edge *embed_graph, int n)
{
    int          i;

    for (i = 0; i < n; i++)
    {
        embedg_dlcl_delete(embed_graph[i].separated_DFS_child_list);
        /*
          embedg_dlcl_delete(embed_graph[i].rep_in_parent_list);

          NO!!! this points to something in separated_DFS_child_list
        */
        embedg_dlcl_delete(embed_graph[i].pertinent_bicomp_list);
    }
    mem_free(embed_graph);
}    



void 
embedg_VES_print (t_ver_edge *embed_graph, int n)
{
    int          i;

    fprintf(stdout, "vertices\n");
    for (i = 0; i < n; i++)
    {
        t_ver_edge   rec;

        rec = embed_graph[i];

        fprintf(stdout, "\nDFI\t%d\tlabel\t%d\n", i, rec.label);
        fprintf(stdout, "DFS parent\t%d\tleast_a\t%d\tlowpoint\t%d\n",
                rec.DFS_parent, rec.least_ancestor, rec.lowpoint);
        fprintf(stdout, "separated_DFS_child_list\n");
        embedg_dlcl_print(rec.separated_DFS_child_list);
    }

    fprintf(stdout, "\nvirtual vertices\n");
    for (i = n; i < 2*n; i++)
    {
        int          c;
        
        c = i - n;
        fprintf(stdout, "%d^%d\t", embed_graph[c].DFS_parent, c);
    }
    fprintf(stdout, "\n");
    
    embedg_VES_print_bigcomps(embed_graph, n);
}


void 
embedg_VES_print_bigcomps (t_ver_edge *embed_graph, int n)
    /*
      walking the external faces of all the bicomp; for testing only
    */
{
    int          i;
 
    fprintf(stdout, "bicomponents\n");
    /*
      to get to the bicomps, it makes sense to start at the
      virtual vertices????
    */
    for (i = n + 1; i < 2*n; i++)
        /*
          a note of caution: there is no virtual vertex at
          embed_graph[n] since that would mean a virtual vertex x^0
          which makes no sense (0 is the root of the dfs_tree)
        */
    {
        embedg_VES_walk_bicomp(embed_graph, n, i, 0);
    }
    fprintf(stdout, "\n");
}
/*
 *  planar_alg_init.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Initialising the embed_graph aka VES data structure from the information
  collected from the DFS.

  The embed_graph/VES data structure is an array consisting of vertices,
  virtual vertices and edges;
  vertices, virtual vertices and edges share a common record structure;
  one of the particular features is that any vertex is linked
  together with its incident edges into a doubly circular linked list.

  See also VES_misc.c.

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/
 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_DFS(x) {}
#define IF_VERB(x)   {}
#define IF_DEB_TREE(x)    {}
#define IF_CPU(x) {}
 
 
/* aproto: header embed_graph_protos.h */

/* aproto: beginstatic -- don't touch this!! */
static void embedg_init_insert_TE (t_ver_edge *, int, int *, t_dlcl *);
/* aproto: endstatic -- don't touch this!! */
 
#ifndef PLANAR_IN_MAGMA
#endif


t_ver_edge *
embedg_planar_alg_init (
    t_ver_sparse_rep *V,
    int n,
    t_adjl_sparse_rep *A,        /* input sparse graph */
    int *nbr_c,        /* size of the graph, #components*/
    int *edge_pos,        /* pos in the struct where the last edge
                                      has been inserted
                                   */
    t_dlcl ***dfs_tree,      /* a sparse graph rep. for the dfs tree
                                      -- vertices are as DFIs
                                   */
    t_dlcl ***back_edges,    /* for each vertex v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v
                                      -- vertices are as DFIs
                                   */
    t_dlcl ***mult_edges    /* for each vertex v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v
                                      -- vertices are as DFIs
                                   */
)
    /*
      initialising embed_graph, the fundamental data structure
      underpinning the tester and obstruction isolator

      from there on, a vertex is exclusively referred to by its DFI!!
      -- so forget about labels
    */
{
    int          *dfs_nbr;        /* dfs numbering for each vertex */
    int          *dfs_order;      /* vertices in dfs order */
    int          *lowpoint;       /* lowpoint value for each DFI */
    int          *dfs_parent;     /* for each DFI, its DFS ancestor 
                                     as a DFI (DFS index)
                                  */
    int          *least_a;        /* for each DFI, its least ancestor's DFI
                                     (via a back edge exclusively)
                                  */

    t_ver_edge   *embed_graph;
    int          i;


  IF_CPU(
    float      sttime;  float time_to_now;
 
    sttime = time_current_user();
       )

    ASSERT(n >= 1);

    /*
      DFS and lowpoint calculations + ordering 
    */
    sparseg_adjl_dfs_preprocessing(V, n, A, nbr_c,
                                   &dfs_nbr, &dfs_order, &lowpoint,
                                   dfs_tree, back_edges,
                                   &dfs_parent, &least_a, mult_edges);

    IF_CPU(
           fprintf(stdout, "CPU for DFS only %f\n",
                   (time_current_user() - sttime));
    sttime = time_current_user();
           )
 
    IF_DEB_DFS(     
            fprintf(stdout, "DFS indices\n");
            for (i = 0; i < n; i++)
            fprintf(stdout, "%d ", dfs_nbr[i]);
            fprintf(stdout, "\n");
            
            fprintf(stdout, "DFS order\n");
            for (i = 0; i < n; i++)
            fprintf(stdout, "%d ", dfs_order[i]);
            fprintf(stdout, "\n");
            
            fprintf(stdout, "lowpoint values\n");
            for (i = 0; i < n; i++)
            fprintf(stdout, "%d ", lowpoint[i]);
            fprintf(stdout, "\n");
            );
    
    IF_VERB(
            fprintf(stdout, "DFS parent\n");
            for (i = 0; i < n; i++)
                fprintf(stdout, "%d ", dfs_parent[i]);
            fprintf(stdout, "\n");
            );

    IF_VERB(
            fprintf(stdout, "least ancestors\n");
            for (i = 0; i < n; i++)
                fprintf(stdout, "%d ", least_a[i]);
            fprintf(stdout, "\n");
            );
    
    IF_VERB(
            for (i = 0; i < n; i++)
            {
                fprintf(stdout, "the list of children ordered by lowpoint for %d\n",
                        i);
                embedg_dlcl_print((*dfs_tree)[i]);
            }
            );

    IF_DEB_DFS(
            fprintf(stdout, "the tree edges\n");
            sparseg_dlcl_print(*dfs_tree, n);
            
            fprintf(stdout, "the back edges\n");
            sparseg_dlcl_print(*back_edges, n);
 
            fprintf(stdout, "multiple edges\n");
            sparseg_dlcl_print(*mult_edges, n);
            );
    
    /*
      create the data structure for the embedded graph:
      it will have (max) size 2*n + 2 * MAXE(n)

      we will see that that number of edges is sufficient
      even when later adding short-cut edges (see embedg_walkdown)
    */
    embed_graph = (t_ver_edge *) mem_malloc(sizeof(t_ver_edge)
                                            * (2*n + 2 * MAXE(n)));
    /*
      initialisation
    */
    for (i = 0; i < 2*n + 2 * MAXE(n); i++)
        /*
          some fields are initialised to n as n is actually
          an "invalid" value 
        */
    {
        t_ver_edge   rec;
  
        rec.label = NIL;
        rec.DFS_parent = n;
        rec.least_ancestor = n;
        rec.lowpoint = n;
        rec.separated_DFS_child_list = NP;
        rec.rep_in_parent_list = NP;
        rec.pertinent_bicomp_list = NP;
        rec.adjacent_to = n;
        rec.visited = n;
        rec.neighbour = n;
        rec.in_adjl = NIL;
        rec.twin_in_adjl = NIL;
        rec.mult = 0;
        rec.type = NIL;
        rec.sign = NILSIGN;
        /*
          make the links refer back to self
        */
        rec.link[0] = rec.link[1] = i;
 
        embed_graph[i] = rec;
    }
    
    /*
      embed_graph[0..n-1]: the n vertices
      ATTENTION: the vertices are stored according to their DFS numbering
    */
    for (i = 0; i < n; i++)
    {
        t_ver_edge   rec;

        rec = embed_graph[i];

        rec.label = dfs_order[i];
        rec.DFS_parent = dfs_parent[i];
        rec.least_ancestor = least_a[i];  
        rec.lowpoint = lowpoint[i];
        rec.separated_DFS_child_list = embedg_dlcl_copy((*dfs_tree)[i]);

        IF_VERB(
                fprintf(stdout, "the list of children ordered by lowpoint for DFI %d\n",
                        i);
                embedg_dlcl_print(rec.separated_DFS_child_list);
        );

        embed_graph[i] = rec;
    }
    
    /*
      one more thing to do for these vertices:
      fix the rep_in_parent_list field
    */
    for (i = 1; i < n; i++)
    {
        t_dlcl       *parent_list, *rep;
        int          parent;

        parent = embed_graph[i].DFS_parent; /* careful: this is a DFI  */
        /*
          recall that the vertices in embed_graph are accessed via their DFI
        */

        if (parent != n)
            /*
              when parent == n this means that i the root of a DFS tree
              in the disconnected graph
            */
        {
            parent_list = embed_graph[parent].separated_DFS_child_list;
            rep = embedg_dlcl_find(parent_list, i);
            ASSERT(rep != NP);
            embed_graph[i].rep_in_parent_list = rep;
        }
    }

    /*
      embed_graph[n..2*n-1]: the n virtual vertices
      do I need to do anything here?????

      no - I don't think so

      let's try to explain what virtual vertices are:
      let v^c be a virtual vertex:
      - it is at position  c + n in the array,
      - c is the DFS child of v,
      - v can be retrieved by taking embed_graph[c].DFS_parent,
      - v^c is said virtual as long as the bicomp rooted by v^c is not
        merged with the vertex v
      - once v is merged (identified?) with v^c, then v^c
        is of no relevance anymore

      below we will see that we embed all the tree edges as singleton
      bicomps (bicomponent): (0^1, 1), (1^2, 2) etc...:
      this is what virtual vertices are there for:
      to distinguish them from their "real" counterpart with
      which they will be ultimately merged

      the primary reason for this is:
      while testing for planarity virtual vertices are the roots of bicomps
    */

    /*
      now the edges:
      we actually embed the tree edges so that each tree edge
      forms a (singleton) biconnected component

      embedding an edge in effect means creating the
      doubly linked circular list of [virtual] vertices & the edges incident
      to it

      this list is built using the links 0 & 1 in embed_graph[i]
    */

    /*
      for each tree edge (v,u) we embed (v^u, u) (v^u is the virtual vertex)

      CAREFUL: when talking about vertex v say, 
      we mean the vertex with DFI v, and NOT the vertex with label v
      **************************************************************
    */
    *edge_pos = 2*n - 1;
    /*
      edge_pos will tell us where to insert the next edge in embed_graph[]
    */
    for (i = 0; i < n; i++)
    {
        t_dlcl     *te_l, *p;

        te_l = (*dfs_tree)[i];
        p = te_l;
 
        if (!embedg_dlcl_is_empty(p))
        {
            /*
              the test below is a bit stupid... well...
            */
            ASSERT(embed_graph[p->info].DFS_parent == i);

            embedg_init_insert_TE(embed_graph, n, edge_pos, p);
            p = embedg_dlcl_list_next(p);
            while (p != te_l)
            {
                ASSERT(embed_graph[p->info].DFS_parent == i);
                embedg_init_insert_TE(embed_graph, n, edge_pos, p);

                p = embedg_dlcl_list_next(p);
            }
        }
    }
    
    mem_free(dfs_nbr);
    mem_free(dfs_order);
    mem_free(lowpoint);

    mem_free(dfs_parent);
    mem_free(least_a);
    
    IF_CPU(
           fprintf(stdout, "CPU for remainder of initialisation %f\n",
                   (time_current_user() - sttime));
           )

    return embed_graph;
}


static void
embedg_init_insert_TE (t_ver_edge *embed_graph, int n, int *edge_pos, t_dlcl *p)
    /*
      init and insert a tree edge in embed graph:
      
      the tree edge will form a singleton bicomponent (v^c, c)
      where c is p->info and v is c.DFS_parent
    */
{
    int             c, v;

    c = p->info;
    v = embed_graph[c].DFS_parent;
    ASSERT(v >= 0 && v < n);
    
    /*
      now (v, c) is a tree edge; embed the directed edge [v^c, c]
      
      -- and recall that v^c is a virtual vertex, at position  c + n
      in embed_graph, and that vertex c is at position c
    */
    
    /*
      first, set this edge with the appropriate info
    */
    (*edge_pos)++;
    ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
    embed_graph[*edge_pos].neighbour = c;
    embed_graph[*edge_pos].in_adjl = p->in_adjl;
    embed_graph[*edge_pos].twin_in_adjl = p->twin_in_adjl;

    ASSERT(p->mult % 2 == 0);
    /*
      we want the number of undirected edges
    */
    embed_graph[*edge_pos].mult = p->mult / 2;
    embed_graph[*edge_pos].type = TE;
    embed_graph[*edge_pos].sign = CCLOCKW;
        
    /*
      link this with vertex v^c in a doubly linked circular list
    */
    embed_graph[c + n].link[0] =
        embed_graph[c + n].link[1] = *edge_pos;
    embed_graph[*edge_pos].link[0] =
        embed_graph[*edge_pos].link[1] = c + n;
        
    /*
      now create/set the twin edge, the directed edge [c, v^c] 
    */
    (*edge_pos)++;
    ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
    embed_graph[*edge_pos].neighbour = c + n;
    embed_graph[*edge_pos].in_adjl = p->twin_in_adjl;
    embed_graph[*edge_pos].twin_in_adjl = p->in_adjl;
    embed_graph[*edge_pos].mult = p->mult / 2;
    embed_graph[*edge_pos].type = TE;
    embed_graph[*edge_pos].sign = CCLOCKW;
    
    /*
      and link it with vertex c in a doubly linked circular list
    */
    embed_graph[c].link[0] = embed_graph[c].link[1] = *edge_pos;
    embed_graph[*edge_pos].link[0] =
        embed_graph[*edge_pos].link[1] = c;
}
/*
 *  dfs_preprocessing.c
 */
 
/*
  What:
  *****
  
  Implementing:

  A DFS as an initialisation step for the planarity tester.
  This is an especially beefed up DFS that collects lots of
  marginal information:

  - a DFS tree as a list of DFS children for each vertex
  - the DFS children are sorted according to their lowpoint value
  - a back_edge structure as a list of descendants v for each
    vertex u such that [v, u] is a back edge
  - a multiple edges structure which stores multiple (directed) edges
    NOT in the DFS tree nor in the back_edge struc, and loops
    
  - the vertices in DFS order
  - the DFS index (DFI) for each vertex
  - the lowpoint value for each vertex
  - the number of components of the (possibly disconnected) graph
  - for each vertex, its DFS parent
  - for each vertex v, its least ancestor u such that [v, u]
    is a back edge

  ALL info above (except the vertices in DFS order) is given
  in terms of the vertices' DFIs and NOT their labels.
  

    
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 
 
  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca
 
 
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********
 
  Paulette Lieby (Magma), Brendan McKay (ANU)
 
  Started October 2001
*/


/*
  There are some dodgy things which need some thought; it would be nice
  to fix them so that the code get's cleaner:

  - we do store in_adj (and twin_in_adjl) for each directed edge:
  
    + this is ONLY needed at the time of recovering the embedding
      (see embedg_recover_embedding) and its sole use is to establish
      a link between an edge in the embedding structure
      t_embed_sparse_rep  *E and the corresponding edge
      in the t_adjl_sparse_rep   *A struc.

    + well, I cannot recall why I thought this correspondence
      was needed in the first place and it might well be the case
      that there is no use for it; in which case recovering the
      embedding is simplified
      (we would store the end-vertex in the embedding's edges instead
      of their index in the adjacency list)

  - there are some non-linear bits in the DFS below: when searching
    for an already existing tree/back/multiple edge.
    I couldn't fix this in less then one hour so I leave it as it is...
    for now.

    This shouldn't be a major issue, overall timings of the planarity
    tester do not show this non-linear "bump"...

  - also, this algorithm has been growing incrementally and I now
    realise that I am using some redundant data structures:
    for example visited[] and the vertex and could be dispensed with...
    ...more things to clean up...

    Paulette  07/02/02
*/
 

#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
#define IF_DEB_TREE(x)    {}
 
 
/* aproto: header embed_graph_protos.h */
 
#ifndef PLANAR_IN_MAGMA
#endif
 

void 
sparseg_adjl_dfs_preprocessing (
    t_ver_sparse_rep *V,
    int n,                /* size of the graph */
    t_adjl_sparse_rep *A,        /* input sparse graph */
    int *c,               /* nbr of components */
    int **dfs_nbr,        /* dfs numbering for each vertex */
    int **dfs_order,      /* vertices in dfs order */
    int **lowpoint,       /* lowpoint value for each DFI */
    t_dlcl ***dfs_tree,      /* a sparse graph rep. for the dfs tree:
                                      for each DFI, a list of its children's
                                      DFI ordered wrt their lowpoint values
                                   */
    t_dlcl ***back_edges,    /* for each DFI  v, a dlcl
                                      of the back edges [v, x] incident to v
                                      where x is a DESCENDANT of v */
    int **dfs_parent,     /* for each DFI its DFS ancestor */
    int **least_a,        /* for each DFI, its least ancestor's DFI
                                      via a back edge exclusively */
    t_dlcl ***mult_edges    /*  for each DFI  v, a dlcl
                                       of the multiple directed
                                       edges NOT included
                                       in either dfs_tree or back_edges
                                   */
)

    /*
      in ALL the returned info above BUT dfs_order[] we store
      the vertices' DFIs (DFS indices) and NOT their labels!

      -- shuffling between labels and vertices can then be done
         via dfs_nbr[] and dfs_order[]
    */
{
    int            pos_v_stack, pos_e_stack, dfs_n;
    int            *visited, *vertex_stack, *edge_stack, *lowpoint_order;
    int            *TE_in_adjl, *TE_twin_in_adjl, *TE_mult;
    int            v, lp, cur, cur_e, next;
    t_dlcl         **temp, *lowpoint_list, **new_dfs_tree;
    
    /*
      create the dfs tree as a sparse graph
    */
    *dfs_tree = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
    /*
      the DFS numbering for the vertices
    */
    *dfs_nbr = (int *) mem_malloc(sizeof(int) * n);
    /*
      the vertices as ordered by their DFS index
    */
    *dfs_order = (int *) mem_malloc(sizeof(int) * n);
    /*
      the lowpoint value for each DFI
    */
    *lowpoint = (int *) mem_malloc(sizeof(int) * n);

    /*
      the (directed) back edges
    */
    *back_edges = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);


    /*
      the DFS parent for each DFI
    */
    *dfs_parent = (int *) mem_malloc(sizeof(int) * n);
    /*
      the least ancestor (via a back edge exlusively) for each DFI
    */
    *least_a = (int *) mem_malloc(sizeof(int) * n);
    
    /*
      the (directed) multiple edges
    */
    *mult_edges = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);

    /*
      the vertices visited while DFS
    */
    visited = (int *) mem_malloc(sizeof(int) * n);
    /*
      stack of vertices: last current vertex
    */
    vertex_stack = (int *) mem_malloc(sizeof(int) * n);
    /*
      stack of (tree) edges: last added tree edge
    */
    edge_stack = (int *) mem_malloc(sizeof(int) * n);
    
    /*
      the following will be used in order to recreate the dfs_tree
      so that the DFS children of each DFI are ordered
      according to their lowpoint value
    */
    lowpoint_order = (int *) mem_malloc(sizeof(int) * n);
    temp = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
    new_dfs_tree = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);

    /*
      finally, three more holding arrays: a trick to remember which
      tree edges we are talking about:

      when constructing dfs_tree, back_edges, mult_edges
      - we NEED to record the index in A (the adjacency list)
        of some of the edges and their twins/inverses
        we are currently storing in either of these structures
      - we also need to record the number of multiple (directed)
        edges we encounter  when the graph is not simple
      
      this is easy to do when storing back edges and multiple edges,
      and tree edges also: but this lattest set of neighbour lists (dfs_tree)
      is subsequently reordered so that DFS children are ordered
      wrt lowpoint values;
      - consequently the info about position in adjacency list
      and edge multiplicity are lost in the ordering process

      the two following arrays will remember the info we'll need later
      - more about this below
    */
    TE_in_adjl = (int *) mem_malloc(sizeof(int) * n);
    TE_twin_in_adjl = (int *) mem_malloc(sizeof(int) * n);
    TE_mult = (int *) mem_malloc(sizeof(int) * n);
    
    
    /*
      initialization of the data structures
    */
    for (v = 0; v < n; v++)
    {
        (*dfs_tree)[v] = (*back_edges)[v] = (*mult_edges)[v] = NP;
        visited[v] = TE_mult[v] = 0;
        (*dfs_parent)[v] = (*least_a)[v] = n;
        temp[v] = new_dfs_tree[v] = NP;
        TE_in_adjl[v] = TE_twin_in_adjl[v] = NIL;
        /*
          note that in the 3rd last statement n is considered
          as an "invalid" value;
          will be if importance in the overall algorithm
        */
    }
    
    /*
      the DFS tree is rooted at vertex 0
    */
    dfs_n = -1;
    pos_v_stack = -1;
    pos_e_stack = -1;
    *c = 0;
    for (v = 0; v < n; v++)
    {
        if (visited[v])
            /*
              we come only at this level when looking for
              a new subtree (when graph is disconnected)
            */
        {
            continue;
        }
        else
        {
            (*c)++;
        }
        
        cur = v;
        visited[cur] = 1;
        (*dfs_nbr)[cur] = ++dfs_n;
        (*lowpoint)[(*dfs_nbr)[cur]] = dfs_n;
        (*dfs_order)[dfs_n] = cur;

        cur_e = V[cur].first_edge == NIL ? NIL : V[cur].first_edge;
        while (TRUE)
        {
            if (cur_e != NIL)
            {
                t_dlcl        *existing_e;
                
                next = A[cur_e].end_vertex;
                if (!visited[next])
                    /*
                      adding tree edges (careful: directed edges)
                      
                      AND tree edges are stored as 
                      [dfs_nbr[u], dfs_nbr[cv]]
                      instead of [u, cv]: that is we store the edges
                      according to the vertices' DFIs
                    */
                {
                    IF_DEB_TREE(
                                io_printf("add tree edge %d\t%d\n",
                                          cur+1, next+1);
                                );
                    
                    (*dfs_nbr)[next] = ++dfs_n;
                    (*lowpoint)[(*dfs_nbr)[next]] = dfs_n;
                    (*dfs_order)[dfs_n] = next;
                    
                    sparseg_dlcl_append_to_neigh_list(*dfs_tree, n,
                                                      (*dfs_nbr)[cur],
                                                      (*dfs_nbr)[next],
                                                      NIL);
                    TE_in_adjl[(*dfs_nbr)[next]] = cur_e;
                    TE_mult[(*dfs_nbr)[next]]++;
                    
                    /*
                      we push cur and the edge (cur, cur_e) on their
                      respective stacks
                    */
                    vertex_stack[++pos_v_stack] = cur;
                    edge_stack[++pos_e_stack] = cur_e;
                    
                    /*
                      and mark next as visited
                    */
                    visited[next] = 1;

                    /*
                      update dfs_parent (always deal with DFIs rembember!)
                    */
                    (*dfs_parent)[(*dfs_nbr)[next]] = (*dfs_nbr)[cur];
                    
                    /*
                      the DFS goes one level deeper
                    */
                    cur = next;
                    cur_e = V[cur].first_edge == NIL ?
                        NIL : V[cur].first_edge;
                }
                /*
                  the next three tests deal with multiple edges
                  and loops: apart from storing these (DIRECTED) edges
                  in mult_edges, we also need to update
                  the multipliciaty information about these edges
                */
                else if (sparseg_dlcl_is_adjacent(*dfs_tree, n,
                                                  (*dfs_nbr)[cur],
                                                  (*dfs_nbr)[next],
                                                  &existing_e))
                    /*
                      [cur, next] is a tree edge
                    */
                {
                    sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
                                                      (*dfs_nbr)[cur],
                                                      (*dfs_nbr)[next],
                                                      cur_e);
                    TE_mult[(*dfs_nbr)[next]]++;
                    
                    cur_e = A[cur_e].next; /* next in cur's adjacency list */
                }
                else if (sparseg_dlcl_is_adjacent(*back_edges, n,
                                                  (*dfs_nbr)[next],
                                                  (*dfs_nbr)[cur],
                                                  &existing_e))
                    /*
                      [cur, next] is a back edge
                    */
                {
                    sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
                                                      (*dfs_nbr)[cur],
                                                      (*dfs_nbr)[next],
                                                      cur_e);
                    (existing_e->mult)++;
                    
                    cur_e = A[cur_e].next; /* next in cur's adjacency list */
                }
                else if (next == cur)
                    /*
                      the case of a loop
                    */
                {
                    if (sparseg_dlcl_is_adjacent(*mult_edges, n,
                                                  (*dfs_nbr)[next],
                                                  (*dfs_nbr)[cur],
                                                  &existing_e))
                        /*
                          in this case we must update the multiplicity
                          of this edge: note that the elt. in cur's
                          neighbours list that gets updated is the first
                          in the list

                          dodgy??? certainly, but can't think
                          of a better way to do this

                          eventually it will happen that even myself
                          won't understand what I am doing..........
                        */
                    {
                        (existing_e->mult)++;
                    }
                    sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
                                                      (*dfs_nbr)[cur],
                                                      (*dfs_nbr)[next],
                                                      cur_e);
                    
                    cur_e = A[cur_e].next; /* next in cur's adjacency list */
                }
                else if (sparseg_dlcl_is_adjacent(*dfs_tree, n,
                                                  (*dfs_nbr)[next],
                                                  (*dfs_nbr)[cur],
                                                  &existing_e))
                    /*
                      [next, cur] is a tree edge:
                      that is, [cur, next] is [next, cur]'s twin/inverse:

                      1. if it is the first time one encounters
                         [cur, next] (as it would always be the case
                         for a simple graph) then all I need to do
                         is to update the tree edge's multiplicity,
                         and the twin info in TE_[]

                      2. if [cur, next] is actually a multiple edge,
                         then I'll need to store it in mult_edges;
                         and I update the tree edge's multiplicity too.
                         No twin info will be required here.
                         Why? see how recover.c embeds the multiple
                         edges in the planar embedding.

                      3. how do I know it is the first time I encounter
                         [cur, next]?:
                         when TE_twin_in_adjl = NIL

                      4. finally, note that the present counting scheme
                         implies that the mult field always holds
                         the number of directed edges:
                         ie, if [a, b] is a tree edge, [a, b].mult = 2
                         because we would have counted [a, b] and [b, a]

                         this applies to tree edges, back edges, and loops
                    */
                {
                    ASSERT(TE_in_adjl[(*dfs_nbr)[cur]] != NIL);
                    if (TE_twin_in_adjl[(*dfs_nbr)[cur]] == NIL)
                    {
                        TE_twin_in_adjl[(*dfs_nbr)[cur]] = cur_e;
                    }
                    else
                    {
                        sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
                                                          (*dfs_nbr)[cur],
                                                          (*dfs_nbr)[next],
                                                          cur_e);
                    }
                     
                    TE_mult[(*dfs_nbr)[cur]]++;

                    cur_e = A[cur_e].next; /* next in cur's adjacency list */
                }
                else if (sparseg_dlcl_is_adjacent(*back_edges, n,
                                                  (*dfs_nbr)[cur],
                                                  (*dfs_nbr)[next],
                                                  &existing_e))
                    /*
                      [next, cur] is a back edge: [cur, next] is its inverse:
                      we proceed as for the tree edge case above
                    */
                {
                    ASSERT(existing_e->in_adjl != NIL);
                    if (existing_e->twin_in_adjl == NIL)
                    {
                        existing_e->twin_in_adjl = cur_e;
                    }
                    else
                    {
                        sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
                                                          (*dfs_nbr)[cur],
                                                          (*dfs_nbr)[next],
                                                          cur_e);
                    }
                    
                    (existing_e->mult)++;
                    
                    cur_e = A[cur_e].next; /* next in cur's adjacency list */
                }
                /*
                  the next bit concludes the DFS: it deals with the case
                  where a back edge needs to be added 
                */
                else 
                    /*
                      that is, next is visited and neither
                      the tree edge [next, cur] nor
                      the back edge [next, cur] exist:
                      
                      this implies that [cur, next] is a back edge
                      that must be added to the back_edges structure
                      (with dfs_nbr(next) < dfs_nbr(cur))
                    */
                {
                    IF_DEB_TREE(
                                io_printf("add back edge %d\t%d\n",
                                          cur+1, next+1);
                                );
                    
                    ASSERT(visited[next]);
                    ASSERT((*dfs_nbr)[cur] > (*dfs_nbr)[next]);
                    
                    sparseg_dlcl_append_to_neigh_list(*back_edges, n,
                                                      (*dfs_nbr)[next],
                                                      (*dfs_nbr)[cur],
                                                      cur_e);

                    /*
                      update cur's lowpoint
                    */
                    (*lowpoint)[(*dfs_nbr)[cur]] =
                        (*dfs_nbr)[next] < (*lowpoint)[(*dfs_nbr)[cur]] ?
                        (*dfs_nbr)[next] : (*lowpoint)[(*dfs_nbr)[cur]];
                    
                    /*
                      update least_a (of cur)
                      (always deal with DFIs remember!)
                    */
                    (*least_a)[(*dfs_nbr)[cur]] =
                        (*dfs_nbr)[next] < (*least_a)[(*dfs_nbr)[cur]] ?
                            (*dfs_nbr)[next] : (*least_a)[(*dfs_nbr)[cur]];

                    /*
                      get the next edge in cur's adjacency list
                    */
                    cur_e = A[cur_e].next;
                }
            }
            
            if (cur_e == NIL)
                /*
                  we are either at a leaf or have finished scanning
                  cur's adjacency list: backtrack
                */
            {
                if (pos_v_stack == -1)      /* no previous vertex */
                {
                    /*
                      no edge left on the stack: DFS ends for
                      this subtree:
                      we visit the next vertex
                    */
                    ASSERT(pos_e_stack == -1);
                    break;
                }
                else
                {
                    int      prev_e;
                    /*
                      Otherwise backtrack and pop cur from the stack
                      as well as the last tree edge  added to the tree.
                      We use next to get a new lowpoint value for cur:
                      This value will be min(lowpoint(cur), lowpoint(next)).
                    */
                    cur = vertex_stack[pos_v_stack--];
                    prev_e = edge_stack[pos_e_stack--];
                    next = A[prev_e].end_vertex;
                    (*lowpoint)[(*dfs_nbr)[cur]] =
                        (*lowpoint)[(*dfs_nbr)[cur]]
                        < (*lowpoint)[(*dfs_nbr)[next]] ?
                        (*lowpoint)[(*dfs_nbr)[cur]]
                        : (*lowpoint)[(*dfs_nbr)[next]];

                    cur_e = A[prev_e].next;
                }
                /*
                  we proceed with DFS
                */
            }
        }
    }
    mem_free(vertex_stack);
    mem_free(edge_stack);

    /*
      just for the sake of it, check that all vertices have
      been visited
    */
#ifdef ASSERTIONS
    for (v = 0; v < n; v++)
    {
        ASSERT(visited[v]);
    }
#endif
    mem_free(visited);

    /*
      we now order the DFIs wrt lowpoint values:
      use bucket sort (linear time)
    */
    /*
      for each lowpoint value, collect the DFIs (in a t_dlcl)
      with that lowpoint value
      (IMPORTANT: we want the DFIs since the aim is to rewrite dfs_tree
      which stores DFIs and not labels!)
    */
    for (v = 0; v < n; v++)
        /*
          v  is taken as a DFI here
        */
    {
        t_dlcl    *r;
 
        r = embedg_dlcl_rec_new(v);
        temp[(*lowpoint)[v]] = 
            embedg_dlcl_rec_append(temp[(*lowpoint)[v]], r);
    }
 
    /*
      concatenate these lists now
    */
    lowpoint_list = temp[0];
    for (lp = 1; lp < n; lp++)
    {
        lowpoint_list = embedg_dlcl_cat(lowpoint_list, temp[lp]);
    }
    ASSERT(embedg_dlcl_length(lowpoint_list) == n);
        
    lowpoint_order[0] = lowpoint_list->info;
    for (lp = 1; lp < n; lp++)
    {
        lowpoint_list = embedg_dlcl_list_next(lowpoint_list);
        lowpoint_order[lp] = lowpoint_list->info;
    }
    embedg_dlcl_delete(lowpoint_list);
    mem_free(temp);

    IF_DEB(
           fprintf(stdout, "dfs_preprocessing, lowpoint_order\n");
           for (lp = 0; lp < n; lp++)
           fprintf(stdout, "%d ", lowpoint_order[lp]);
           fprintf(stdout, "\n");
           fprintf(stdout, "dfs_preprocessing, lowpoint\n");
           for (lp = 0; lp < n; lp++)
           fprintf(stdout, "%d ", (*lowpoint)[lp]);
           fprintf(stdout, "\n");
           )
    
    /*
      we now use this order to rewrite dfs_tree such that
      the DFS children of each vertex are ordered wrt lowpoint values
    */
    for (lp = 0; lp < n; lp ++)
        /*
          for each DFI in lowpoint_order[] I know its DFS_parent
          from dfs_parent[] -- the rest is then trivial
        */
    {
        int       parent;
        
        v = lowpoint_order[lp];
        /*
          lowpoint_order stores DFIs as does dfs_parent, so the lot
          makes sense
        */
        parent = (*dfs_parent)[v];
        if (parent != n)
            /*
              v may be the root of a DFS tree
            */
        {
            t_dlcl   *temp;

            temp = embedg_dlcl_rec_new(v);

            /*
              this is where the TE_ holding arrays are useful *sigh*
            */
            ASSERT(TE_in_adjl[v] != NIL);
            temp->in_adjl = TE_in_adjl[v];

            ASSERT(TE_twin_in_adjl[v] != NIL);
            temp->twin_in_adjl = TE_twin_in_adjl[v];

            ASSERT(TE_mult[v] != 0 && TE_mult[v] % 2 == 0);
            temp->mult = TE_mult[v];
            
            new_dfs_tree[parent] =
                embedg_dlcl_rec_append(new_dfs_tree[parent], temp);
        }
    }
    mem_free(lowpoint_order);
    mem_free(TE_in_adjl);
    mem_free(TE_twin_in_adjl);
    mem_free(TE_mult);

    /*
      some checks are in order here
    */
#ifdef ASSERTIONS
    for (v = 0; v < n; v++)
    {
        ASSERT(embedg_dlcl_length((*dfs_tree)[v])
               == embedg_dlcl_length(new_dfs_tree[v]));

        IF_DEB(
               fprintf(stdout, "dfs_preprocessing    dfs_tree for %d\n", v);
               embedg_dlcl_print((*dfs_tree)[v]);
               fprintf(stdout, "dfs_preprocessing    new_dfs_tree for %d\n", v);
               embedg_dlcl_print(new_dfs_tree[v]);
    );
    }
#endif
    
    sparseg_dlcl_delete(*dfs_tree, n);
    *dfs_tree = new_dfs_tree;
}

/*
 *  embedding.c
 */
 
/*
  What:
  *****
  
  Implementing:

  The graph is planar: we recover the embedding from the VES structure
  and check it as well.
  (Some of these checks will disappear later)


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_EMBED(x)    {}
#define IF_DEB_CHECK_EMBED(x)    {}
#define IF_DEB_FACES(x) {}
#define IF_VERB(x)   {}
#define IF_DEB_SCE(x) {}
#define IF_CPU(x) {}
 

/* aproto: header embed_graph_protos.h */
 
 
#ifndef PLANAR_IN_MAGMA
#endif
 
void 
embedg_embedding (t_ver_sparse_rep *V, t_adjl_sparse_rep *A,
        t_ver_edge *embed_graph, int n, int e, int nbr_c,
        int edge_pos, t_dlcl **mult_edges, t_ver_sparse_rep **vertices,
        t_embed_sparse_rep **embedding)
    /*
      recovering the embedding for the (planar) graph

      - the embedding is returned in vertices and embedding, vertices
      indexes embedding, the ordered list of edges
      - edges in the embedding are given as their index in A, the graph's
      adajacency list
      - the nbr of edges in the embedding is given as nbr_e_embed:
      this may be different form the original number of edges when the graph
      iss not simple
    */
{
    int          *ver_orient, nbr_comp, nbr_e_embed;
    
    IF_CPU(
    float      sttime; float time_to_now;
 
    sttime = time_current_user();
    )

    IF_DEB(
           fprintf(stdout, "embedding, begin, which edges have been flipped\n");
           embedg_VES_print_flipped_edges(embed_graph, n, edge_pos);
           )

    IF_DEB(
           fprintf(stdout, "embedding, before removing SCE\n");
           embedg_VES_print_bigcomps(embed_graph, n);
           )

    /*
      several things to do:
      1. removing the short-cut edges
    */
    embedg_remove_SCE(embed_graph, n, edge_pos);

    IF_DEB(
           fprintf(stdout, "embedding, after removing SCE\n");
           embedg_VES_print_bigcomps(embed_graph, n);
           )
        
    /*
      2. computing each vertex's orientation (wrt flipped bicomps)
    */
    ver_orient = embedg_vertices_orientation(embed_graph, n);
    

    /*
      3. merging the remaining virtual vertices with their
         non-virtual counterpart
    */
    nbr_comp = embedg_merge_remaining_virtual(embed_graph, n);
    /*
      actually there is no need to return the nbr of components
      from the above function
      but let's do it for the sake of it and for possible checking
    */
    ASSERT(nbr_c == nbr_comp);

    IF_DEB(
           fprintf(stdout, "embedding, after merging of remaining vertices\n");
           )

    /*
      4. to be on the safe side: check that the embedding is a valid one

      for now, we DIE if not
    */

    if (!embedg_is_embed_valid(embed_graph, n, nbr_comp, edge_pos,
                               ver_orient, &nbr_e_embed))
    {
        mem_free(ver_orient);
        DIE();
    }
    mem_free(ver_orient);

    ASSERT(nbr_e_embed <= e);
    /*
      when the graph is not simple, multiple edges and loops are
      not in embed_graph[]: they will be added to the final
      embedding in embedg_recover_embedding below
    */
    
    /*
      5. recover the embedding in preparation for the Magma type,
      and check it as well
    */
    embedg_recover_embedding(V, A, embed_graph, n, e,
                             mult_edges, vertices, embedding);
    if (!embedg_check_recov_embedding(n, e, nbr_comp,
                                      *vertices, A, *embedding))
    {
        mem_free(*vertices);
        mem_free(*embedding);
        
        IF_CPU(
               fprintf(stdout, "CPU for embedding recovering %f\n",
                       time_current_user() - sttime);
               )
 
        DIE();
    }

    IF_DEB_EMBED(
                 fprintf(stdout, "embedding, original graph and embedding\n");
                 sparseg_adjl_print(V, n, A, FALSE);
                 fprintf(stdout, "\n");
                 sparseg_adjl_embed_print(*vertices, n, A, *embedding,
                                          FALSE);
                 )
    
    IF_CPU(
           fprintf(stdout, "CPU for embedding recovering %f\n",
                   time_current_user() - sttime);
           )
}


void 
embedg_remove_SCE (t_ver_edge *embed_graph, int n, int edge_pos)
    /*
      remove all the short-cut edges from the embedding
    */
{
    int          i, c;

    c = 0;
    for (i = 2*n; i <= edge_pos; i += 2)
        /*
          and edge and its twin occupy consecutive positions in embed_graph:
          need only to examine one out of two
          (removing an edge also entails removing its twin of course
        */
    {
        if (embedg_VES_is_short_cut_edge(embed_graph, n, i))
        {
            IF_DEB_SCE(
                       fprintf(stdout, "remove SCE\n");
                       embedg_VES_print_edge(embed_graph, n, i);
                       )

            embedg_VES_remove_edge(embed_graph, n, i);
            c++;
        }
    }

    IF_DEB_SCE(
               fprintf(stdout, "nbr of SCE edges removed %d\n", c);
               )
}


int *
embedg_vertices_orientation (t_ver_edge *embed_graph, int n)
    /*
      for each vertex return its orientation from the
      bicomps in embed_graph:
      perform a DFS of each bicomp
    */
{
    int          i, vv, prod_sign;
    int          *stack, *ver_orient, to_prev;

    /*
      the whole lot makes sense iff the adjacency lists are consistent:
      this is a very important issue and it might be the case
      that the ASSERT warrants replacement by a DIE
      (the check is linear - I think)
    */
    ASSERT(embedg_VES_are_adj_lists_consistent(embed_graph, n));

    ver_orient = (int *) mem_malloc(sizeof(int) * n);
    for (i = 0; i < n; i++)
    {
        ver_orient[i] = CCLOCKW;
    }

    /*
      create the stack for the DFS
    */
    stack = (int *) mem_malloc(sizeof(int) * 3*n);
    to_prev = -1;
    
    IF_DEB(
           fprintf(stdout, "vertex orientation, one line (of vert.) for each bicomp\n");
           )
                
    /*
      now visit all the bicomps, ie, all the virtual vertices
      in embed_graph
    */
    for (vv = n; vv < 2*n; vv++)
    {
        int      c, cur, cur_e;
        boolean  NEW_BICOMP;

        if (embed_graph[vv].link[0] == vv)
            /*
              means that vv is disabled and is not the root of a bicomp
            */
        {
            continue;
        }

        c = vv - n;
        IF_DEB(
               fprintf(stdout, "%d ", c);
               )
        /*
          orientation for c (vv is as yet unembedded) is CCLOCKW

          now find the orientation of all its DFS descendants
        */

        if (embed_graph[c].DFS_parent == n)
            /*
              this means that actually c is an isolated vertex:
              we initialise the sign to CCLOCKW
            */
        {
            prod_sign = CCLOCKW;
        }
        else
            /*
              we initialise the sign to CCLOCKW to the sign of c's parent
            */
        {
            prod_sign = ver_orient[embed_graph[c].DFS_parent];
        }

        /*
          we must not forget to set c's sign!!
          (won't be done below)
        */
        ver_orient[c] = prod_sign;
        
        NEW_BICOMP = FALSE;
        cur = c;
        cur_e = embed_graph[cur].link[0];
        ASSERT(embedg_VES_is_edge(n, cur_e));

        ASSERT(to_prev == -1);
        while (TRUE)
        {
            while (!embedg_VES_is_tree_edge(embed_graph, n, cur_e)
                   || !embedg_VES_is_vertex(n,
                                             embed_graph[cur_e].neighbour)
                   || embed_graph[cur_e].neighbour <= cur)
                /*
                  want to find a tree edge [cur, u]
                  where u is a descendant of cur
                */
            {
                cur_e = embed_graph[cur_e].link[0];
                
                while (cur_e == cur)
                    /*
                      back to the vertex where we started from:
                      no edge has been found:
                      cur is a leaf, backtrack
                    */
                {
                    if (to_prev == -1)
                    {
                        NEW_BICOMP = TRUE;
                        break;
                    }
                    prod_sign = stack[to_prev--];
                    cur_e = stack[to_prev--];
                    /*
                      must advance one more edge
                    */
                    cur_e = embed_graph[cur_e].link[0];
                    cur = stack[to_prev--];
                }
                if (NEW_BICOMP)
                {
                    break;
                }
            }

            if (NEW_BICOMP)
            {
                break;
            }
            else
                /*
                  now cur_e is the edge we were looking for, get its sign
                */
            {
                /*
                  push on stack the current vertex, the edge where we
                  stopped the DFS, AND the sign carried by that vertex
                  
                  and go down one level in the DFS
                */
                stack[++to_prev] = cur;
                stack[++to_prev] = cur_e;
                stack[++to_prev] = prod_sign;
                
                cur = embed_graph[cur_e].neighbour;
                prod_sign *= embed_graph[cur_e].sign;
                ver_orient[cur] = prod_sign;
                
                cur_e = embed_graph[cur].link[0];
                ASSERT(embedg_VES_is_edge(n, cur_e));
                
                IF_DEB(
                       fprintf(stdout, "%d with sign %d\n", cur, prod_sign);
                       )
            }
        }

        IF_DEB(
               fprintf(stdout, "\n");
               )
    }

    IF_DEB(
           fprintf(stdout, "vertex orientation\n");
           for (i = 0; i < n; i++)
           {
               fprintf(stdout, "%d ", ver_orient[i]);
           }
           fprintf(stdout, "\n");
           )

   mem_free(stack);
   return ver_orient;
}
    

int 
embedg_merge_remaining_virtual (t_ver_edge *embed_graph, int n)
    /*
      after the short-cut edges have been removed and the vertices'
      orientation computed, one finishes by merging all
      remaining virtual vertices with their virtual counterpart
      (without flip of course)

      and use this routine to return  the number of disconnected
      components of the graph
    */
{
    /*
      at this stage it is easy to see that all remaining
      virtual vertices are DFS roots (if the graph is not connected)
      or cut vertices
    */

    int          vv, nbr_comp;

    nbr_comp = 0;
    for (vv = n; vv < 2*n; vv++)
    {
        int      v, c;


        c = vv - n;
        v = embed_graph[c].DFS_parent;
        
        /*
          must fish out which virtual vertices are actual roots
          of DFS trees (esp. for the disconnected graph case):
          roots of DFS trees are those virtual vertices for which
          v = embed_graph[c].DFS_parent = n
        */
        if (v == n)
        {
            nbr_comp++;
            continue;
        }
        
        if (embed_graph[vv].link[0] == vv)
            /*
              means that vv is disabled and is not the root of a bicomp
            */
        {
            continue;
        }

        embedg_VES_merge_simple_bicomps(embed_graph, n,
                                                   vv, 1, v, 0);
        /*
          note:
          since v is a cut vertex in this intance the bicomp
          rooted by vv will be merged without flip;
          therefore we could have done
          embedg_VES_merge_simple_bicomps(embed_graph, n,
          vv, 0, v, 1)
          as well, the important thing being that vin != vvout
          (see embedg_VES_merge_simple_bicomps)
        */
    }

    return nbr_comp;
}


int 
embedg_nbr_faces (t_ver_edge *embed_graph, int n, int edge_pos,
        int *ver_orient, int *nbr_e_embed)
    /*
      count the number of faces and the number of edges of the embedding
    */
{
    int          v, e, f, total_e;

    IF_DEB_FACES(
                 int    v;
                 
                 fprintf(stdout, "nbr of faces, the vertices' adj. lists\n");
                 for (v = 0; v < n; v++)
                     embedg_VES_print_adj_list(embed_graph, n,
                                                          v, TRUE);
                 )
        
    /*
      the following is no more than a quick check -- certainly
      not very useful -- or could be done elsewhere
    */
    total_e = 0;
    for (e = 2*n; e <= edge_pos; e++)
    {
        if (!embedg_VES_is_short_cut_edge(embed_graph, n, e))
        {
            total_e++;
        }
    }
    ASSERT(total_e % 2 == 0);
    *nbr_e_embed = total_e / 2;

    /*
      I now set each edge's orientation

      QUESTION: do I really need to do this???
      so far, when doing a proper face traversal, the way in which
      the adjacency list of an edge must be traversed is given
      by the vertex's (in that list) orientation...
      So this seems sensible to me huh?
    */
    embedg_VES_set_orientation(embed_graph, n, ver_orient);

    /*
      I will be using the visited field to enable me to check
      if all edges have been traversed

      let's be smart (?!): so far the visited field has been used
      and set in the following circumstances:
      + initialisation: set to n
      + walkup: set to whatever DFI of interest

      so here we set it to MARK_EMBED(n)
    */
    f = 0;
    for (e = 2*n; e <= edge_pos; e++)
    {
        if (!embedg_VES_is_short_cut_edge(embed_graph, n, e)
            /*
              arrghh!!! I must also skip the SCE!!!
            */
            && embed_graph[e].visited != MARK_EMBED(n))
        {
            int          ein;
            
            IF_DEB_FACES(
                         fprintf(stdout, "nbr of faces, edges not visited\n");
                         embedg_VES_print_edge(embed_graph, n, e);
                         )

            ein = embed_graph[e].sign == CCLOCKW ? 0 : 1;
            /*
              the way I enter e in dependent on its sign:
              all the proper face traversal must obviously be done
              with the same orientation!
            */
            embedg_VES_walk_proper_face(embed_graph, n, e,
                                                       ein,
                                                       TRUE,
                                                       MARK_EMBED(n));
            f++;
        }
    }

    /*
      counting the faces by traversing all the edges does not
      account of the face defined by isolated vertices
      -- we do that now

      we only need to check which vertices refer to self, ie with
      no incident edges
    */
    for (v = 0; v < n; v++)
    {
        if (embed_graph[v].link[0] == v)
        {
            ASSERT(embed_graph[v].link[1] == v);
            f++;
        }
    }
    
    return f;
}


boolean 
embedg_is_embed_valid (t_ver_edge *embed_graph, int n, int nbr_comp,
        int edge_pos, int *ver_orient, int *nbr_e_embed)
    /*
      use Euler's formula to assertain that the embedding is a valid
      embedding:

      f = 2 * nbr_comp + nbr_e_embed - n

    */
{
    int         v, f;

    f = embedg_nbr_faces(embed_graph, n, edge_pos, ver_orient, nbr_e_embed);

    IF_DEB_CHECK_EMBED(
                       fprintf(stdout, "embedding, n: %d\t e: %d\t C: %d\t f: %d\n",
                               n, nbr_e, nbr_comp, f);
                       )
        
    return f == 2 * nbr_comp + *nbr_e_embed - n ? TRUE : FALSE;
}
/*
 *  ext_face_walk.c
 */
 
/*
  What:
  *****
  
  Implementing the external face walk of a bicomponent.
  The concept of an external face --in the context of the VES
  data structure-- makes only sense when talking
  about a bicomp.

  Recall that a vertex is linked together with the edges
  incident from it in a circular (doubly) linked list
  (this is the VES structure).

  One particular feature is that if a vertex v is on
  the external face of a component and if in the list
  we have edges e1, e2 such as e1  -> v -> e2
  then e1 and e2 border the external face.

  In other words, in the circular list of vertex v and edges,
  v is ALWAYS between the two edges bordering the external face

  Of course, when v is (maybe) pushed into the internal face
  (by embedding of some edge) then we don't care about this any more
  (for v that is).
  


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
 
 
/* aproto: header embed_graph_protos.h */
 

void 
embedg_VES_get_succ_on_ext_face (t_ver_edge *embed_graph, int n, int v,
        int vin, boolean MARK, int mark, int *s, int *sin)
    /*
      find the successor s of v (entered via vin) on the external face
      -- also return the direction in which s has been entered

      if MARK true mark the succ. vertex and the edges traversed
      with mark (the visited field)
    */
{
    int        e, twin;
    int        vout, ein, eout, tout;

    ASSERT(embedg_VES_is_vertex(n, v)
           || embedg_VES_is_virtual_vertex(n, v));

    IF_DEB(
           fprintf(stdout, "get_succ_on_ext_face, of %d:%d\n", v, vin);
           )
        
    /*
      find the direction out of the vertex, and get the edge
    */
    vout = vin == 0 ? 1 : 0;
    e = embed_graph[v].link[vout];
    if (embedg_VES_is_virtual_vertex(n, v) && e == v)
        /*
          this can happen if a virtual vertex has been "disabled"

          -- this should not never happen since we can only walk
          on the external face of a bicomp!
        */
    {
        *s = v;
        *sin = vin;
        return;
    }

    /*
      otherwise we must have an edge:
      note that it is entirely irrelevant if I walk SCEs:
      those are precisely there to "jump" over inactive vertices
    */
    ASSERT(embedg_VES_is_edge(n, e));

    /*
      get the twin edge
    */
    twin = embedg_VES_get_twin_edge(embed_graph, n, e);

    IF_DEB(
           fprintf(stdout, "get_succ_on_ext_face, edge [%d, %d]\n",
                   v, embed_graph[e].neighbour);
           fprintf(stdout, "get_succ_on_ext_face, twin edge [%d, %d]\n",
                   embed_graph[e].neighbour, embed_graph[twin].neighbour);
           )
    /*
      find which of twin's link links a vertex
    */
    tout = embedg_VES_is_vertex(n, embed_graph[twin].link[0])
        || embedg_VES_is_virtual_vertex(n,
                                                   embed_graph[twin].link[0])
        ?
        0 : 1;

    /*
      get this vertex: this is v's successor on the external face
    */
    *s = embed_graph[twin].link[tout];

    /*
      one more thing to do: find the direction in which s was entered
    */
    *sin = embed_graph[*s].link[0] == twin ? 0 : 1;

    IF_DEB(
           fprintf(stdout, "get_succ_on_ext_face, succ is %d:%d\n",
                   *s, *sin);
           )
    /*
      a special case: when the bicomp is a singleton bicomp
      (ie a single edge)
    */
    if (embed_graph[*s].link[0] == (embed_graph[*s].link[1]))
    {
        ASSERT(embed_graph[*s].link[0] == twin);
        *sin = vin;
    }

    /*
      finally, mark the vertex and edges if so requested
    */
    if (MARK)
    {
        embed_graph[*s].visited = mark;
        embed_graph[e].visited = mark;
        embed_graph[twin].visited = mark;
    }
}

void 
embedg_VES_get_succ_active_on_ext_face (t_ver_edge *embed_graph, int n,
        int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
    /*
      find the ACTIVE (wrt v) successor s of w (entered via win)
      on the external face
      -- also return the direction in which s has been entered
 
      if MARK true mark the succ. vertex (and the edge)
      with mark (the visited field)
    */
{
    /*
      simply repeatedly calls embedg_VES_get_succ_on_ext_face
      until an active vertex is found
    */
    ASSERT(embedg_VES_is_vertex(n, w)
           || embedg_VES_is_virtual_vertex(n, w));

    embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                         w, win, MARK, mark, s, sin);
    while (embedg_VES_is_ver_inactive(embed_graph, n, v, *s))
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                             *s, *sin, MARK, mark, s, sin);
    }
    ASSERT(!embedg_VES_is_ver_inactive(embed_graph, n, v, *s));
}

void 
embedg_VES_get_succ_ext_active_on_ext_face (t_ver_edge *embed_graph, int n,
        int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
    /*
      find the externally active (wrt v) successor s of w (entered via win)
      on the external face
      -- also return the direction in which s has been entered
 
      if MARK true mark the succ. vertex (and the edge)
      with mark (the visited field)
    */
{
    ASSERT(embedg_VES_is_vertex(n, w)
           || embedg_VES_is_virtual_vertex(n, w));

    embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                         w, win, MARK, mark, s, sin);
    while (!embedg_VES_is_ver_ext_active(embed_graph, n, v, *s))
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                             *s, *sin, MARK, mark, s, sin);
    }
    ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, *s));
}

void 
embedg_VES_get_succ_pertinent_on_ext_face (t_ver_edge *embed_graph, int n,
        int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
    /*
      find the pertinent (wrt v) successor s of w (entered via win)
      on the external face
      -- also return the direction in which s has been entered
 
      if MARK true mark the succ. vertex (and the edge)
      with mark (the visited field)
    */
{
    ASSERT(embedg_VES_is_vertex(n, w)
           || embedg_VES_is_virtual_vertex(n, w));

    embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                         w, win, MARK, mark, s, sin);
    while (!embedg_VES_is_ver_pertinent(embed_graph, n, v, *s))
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                             *s, *sin, MARK, mark, s, sin);
    }
    ASSERT(embedg_VES_is_ver_pertinent(embed_graph, n, v, *s));
}

/*
 *  mark_kur.c
 */
 
/*
  What:
  *****
  
  Implementing:

  Marking the Kuratowski obstruction (in the VES structure):
  this we do once we know which minor we are talking about
  (see isolator.c).
 
 
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 
 
  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca
 
 
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********
 
  Paulette Lieby (Magma), Brendan McKay (ANU)
 
  Started October 2001
*/
 
 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_VERB(x)   {}
#define IF_DEB_TREE(x)    {}
#define IF_DEB_EDGES(x) {}
#define IF_CPU(x) {}
 


/* aproto: header embed_graph_protos.h */

/* aproto: beginstatic -- don't touch this!! */
static void embedg_VES_walk_mark_part_ext_face (t_ver_edge *, int, int, int, int, int, int);
static void embedg_VES_walk_mark_ext_face (t_ver_edge *, int, int, int);
static void embedg_VES_walk_mark_part_proper_face (t_ver_edge *, int, int, int, int, int);
static boolean embedg_VES_is_part_ext_face_marked (t_ver_edge *, int, int, int, int, int, int);
static void embedg_get_u_x (t_ver_edge *, int, int, int, int *);
static int embedg_get_least_neigh (t_dlcl **, t_dlcl **, int, int, int);
static void embedg_add_mark_u_x (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int *, int);
static void embedg_mark_tree_path (t_ver_edge *, int, int, int, int);
static void embedg_add_mark_v_w (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int);
static void embedg_add_mark_v_w_for_B (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int *, int);
static void embedg_mark_x_y_path (t_ver_edge *, int, int *, int *, int, int);
/* aproto: endstatic -- don't touch this!! */
 
#ifndef PLANAR_IN_MAGMA
#endif



static void 
embedg_VES_walk_mark_part_ext_face (t_ver_edge *embed_graph, int n,
        int v, int vin, int from, int to, int mark)
    /*
      walk & mark the external face:
      walk in the direction vin -> v -> vout and mark <from> <to>
    */
{
    int          cur, curin, next, nextin;

    embed_graph[from].visited = mark;
    embed_graph[to].visited = mark;

    IF_DEB(
           fprintf(stdout, "part. ext face marked\t");
           fprintf(stdout, "%d\t", from);
           )

    next = cur = v;
    curin = vin;
    while (next != from)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
                                             FALSE, 0, &next, &nextin);
        cur = next;
        curin = nextin;
    }
    next = n;
    while (next != to)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
                                             TRUE, mark, &next, &nextin);
        cur = next;
        curin = nextin;

        IF_DEB(
               fprintf(stdout, "%d\t", next);
               )
    }
    IF_DEB(
           fprintf(stdout, "\n");
           )
}
   
static void 
embedg_VES_walk_mark_ext_face (t_ver_edge *embed_graph, int n, int v, int mark)
    /*
      walk & mark the external face, starting & ending at vertex v
    */
{
    embedg_VES_walk_mark_part_ext_face(embed_graph, n, v, 0, v, v,
                                            mark);
}
   


static void 
embedg_VES_walk_mark_part_proper_face (t_ver_edge *embed_graph, int n,
        int from_e, int from_ein, int to, int mark)
    /*
      walk & mark a proper face starting at EDGE from_e and ending
      at VERTEX to

      walk in the direction from_ein -> from_e -> to and mark
      everything in between
    */
{
    int     s, cur_e, cur_ein, next_e, next_ein;
 
    next_e = s = n; /* this is an invalid value for an edge/vertex */
  
    cur_e = from_e;
    cur_ein = from_ein;
    while (s != to)
    {
        ASSERT(embedg_VES_is_edge(n, cur_e));
        ASSERT(!embedg_VES_is_short_cut_edge(embed_graph,
                                                        n, cur_e));
 
        embedg_VES_get_succ_on_proper_face(embed_graph, n,
                                                cur_e, cur_ein,
                                                TRUE, mark,
                                                &s, &next_e, &next_ein);
        cur_e = next_e;
        cur_ein = next_ein;
    }
}



static boolean 
embedg_VES_is_part_ext_face_marked (t_ver_edge *embed_graph, int n, int v,
        int vin, int from, int to, int mark)
    /*
      simple check to see if all the vertices on the external
      face walk starting at vin -> v -> vout <from> <to> are marked
      (with mark)
    */
{
    int          cur, curin, next, nextin;

    if (embed_graph[from].visited != mark || embed_graph[to].visited != mark)
        return FALSE;

    cur = v;
    curin = vin;
    next = n;
    while (next != from)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
                                             FALSE, 0, &next, &nextin);
        cur = next;
        curin = nextin;
    }
    while (next != to)
    {
        embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
                                             FALSE, 0, &next, &nextin);
        if (embed_graph[next].visited != mark)
            return FALSE;

        cur = next;
        curin = nextin;
    }
    
    return TRUE;
}


boolean 
embedg_VES_is_ext_face_marked (t_ver_edge *embed_graph, int n, int v, int mark)
    /*
      simple check to see if all the vertices on the external
      face walk starting/ending at v are marked (with mark)
    */
{
    return embedg_VES_is_part_ext_face_marked(embed_graph, n, v, 0,
                                                   v, v, mark);
}


static void 
embedg_get_u_x (t_ver_edge *embed_graph, int n, int v, int x, int *u_x)
    /*
      x is an externally active vertex (wrt v):
      we want u_x, the lowest point of "attachement" for
      the unembedded directed edge [x, u_x]
    */
{
    int          c;
    t_dlcl       *child_list;

    ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, x));
    if (embed_graph[x].least_ancestor < v)
        /*
          then there is a single unembedded back edge (u_x, x),
          u_x an ancestor of v
        */
    {
        *u_x = embed_graph[x].least_ancestor;
        return;
    }

    /*
      else there is a tree path x to d_x and an
      unembedded back edge (u_x, d_x)

      get the lowpoint of the first elt. in separated_DFS_child_list of x
    */
    child_list = embed_graph[x].separated_DFS_child_list;
    ASSERT(!embedg_dlcl_is_empty(child_list));
    c = child_list->info;
    *u_x = embed_graph[c].lowpoint;
}

static int 
embedg_get_least_neigh (t_dlcl **dfs_tree, t_dlcl **back_edges,
        int n, int v, int c)
    /*
      get the least neighbour of v >= c, ie a vertex in the sub tree
      rooted by c

      somehow this must always succeed
    */
{
    int          least_n;
    t_dlcl       *tree_l, *back_l, *p;

    /*
      neighbours are found in either dfs_tree[v] or back_edges[v]
    */

    tree_l = dfs_tree[v];
    back_l = back_edges[v];
    ASSERT(!embedg_dlcl_is_empty(tree_l) || !embedg_dlcl_is_empty(back_l));

    least_n = n;  /* ok, invalid value for any neighbour */
    p = tree_l;
    if (!embedg_dlcl_is_empty(p))
    {
        if (p->info >= c)
        {
            least_n = p->info < least_n ? p->info : least_n;
        }
        p = embedg_dlcl_list_next(p);
        while (p != tree_l)
        {
            if (p->info >= c)
            {
                least_n = p->info < least_n ? p->info : least_n;
            }
            p = embedg_dlcl_list_next(p);
        }
    }
    p = back_l;
    if (!embedg_dlcl_is_empty(p))
    {
        if (p->info >= c)
        {
            least_n = p->info < least_n ? p->info : least_n;
        }
        p = embedg_dlcl_list_next(p);
        while (p != back_l)
        {
            if (p->info >= c)
            {
                least_n = p->info < least_n ? p->info : least_n;
            }
            p = embedg_dlcl_list_next(p);
        }
    }

    ASSERT(least_n >= c);
    /*
      this is so because of the context where this function is called from
    */
    return least_n;
}
  
static void 
embedg_add_mark_u_x (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v,
        int x, int *u_x, int mark)
    /*
      marking a Kuratowski homeomorph:
      
      marking and adding the unembedded dotted edge (u, x),
      x an ext. active vertex wrt v
    */
{
    int          c, d_x;
    t_dlcl       *child_list;

    ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, x));
    if (embed_graph[x].least_ancestor < v)
        /*
          then there is a single unembedded back edge (u_x, x),
          u_x an ancestor of v
        */
    {
        *u_x = embed_graph[x].least_ancestor;
        embed_graph[x].visited = mark;
        embed_graph[*u_x].visited = mark;
        embedg_VES_add_edge(embed_graph, n, edge_pos, *u_x, x,
                                       TRUE, mark);
        return;
    }

    /*
      else there is a tree path x to d_x and an
      unembedded back edge (u_x, d_x)

      get the lowpoint of the first elt. in separated_DFS_child_list of x
    */
    child_list = embed_graph[x].separated_DFS_child_list;
    ASSERT(!embedg_dlcl_is_empty(child_list));
    c = child_list->info;
    *u_x = embed_graph[c].lowpoint;

    /*
      search for the least neighbour of *u_x  >= c,
      that is in the subtree rooted by c
    */
    d_x = embedg_get_least_neigh(dfs_tree, back_edges, n, *u_x, c);
    ASSERT(d_x >= c);
    /*
      this must be true  since u_x is incident to a descendant of x
      (remember: x is externally active)
    */

    /*
      mark the DFS tree path from d_x to x
    */
    embedg_mark_tree_path(embed_graph, n, d_x, x, mark);
    /*
      add the unembedded (u_x, d_x) edge
    */
    embedg_VES_add_edge(embed_graph, n, edge_pos, *u_x, d_x,
                                   TRUE, mark);
}
  
static void 
embedg_mark_tree_path (t_ver_edge *embed_graph, int n, int d_x, int x, int mark)
    /*
      marking the DFS tree path d_x...x where x is an ancestor of d_x
    */
{
    int          cur_v, te, twe;

    ASSERT(d_x >= x);
    
    cur_v = d_x;

    while (cur_v != x)
    {
        embed_graph[cur_v].visited = mark;
        te = embed_graph[cur_v].link[0];
        ASSERT(embedg_VES_is_edge(n, te));
        while (!embedg_VES_is_tree_edge(embed_graph, n, te)
               || (embed_graph[te].neighbour > cur_v
                   && embed_graph[te].neighbour != cur_v + n))
            /*
              want to find a tree edge incident to an ancestor of d_x:
              given that d_x..x is a tree path, we MUST find such an edge!

              note also that I must take account of the fact that
              [te].neighbour could be a virtual vertex, in which case
              it can only be cur_v + n!
            */
        {
            te = embed_graph[te].link[0];
        }
        ASSERT(embedg_VES_is_tree_edge(embed_graph, n, te));
        ASSERT(embed_graph[te].neighbour == embed_graph[cur_v].DFS_parent
               || embed_graph[te].neighbour == cur_v + n);
        
        embed_graph[te].visited = mark;
        twe =  embedg_VES_get_twin_edge(embed_graph, n, te);
        embed_graph[twe].visited = mark;

        /*
          want only to deal with real vertices instead of virtual vertices
        */
        cur_v = embed_graph[te].neighbour < cur_v ?
            embed_graph[te].neighbour : embed_graph[cur_v].DFS_parent;
    }
    embed_graph[x].visited = MARK_MINORS(n);
}
  

static void 
embedg_add_mark_v_w (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v, int w, int mark)
    /*
      marking a Kuratowski homeomorph:
      
      marking and adding the unembedded dotted edge (v, w),
      w is pertinent wrt v
    */
{
    int          vw, c, d_w;
    t_dlcl       *bicomp_list;
    
    if (embed_graph[w].adjacent_to == v)
        /*
          then there is a single unembedded back edge (v, w)
          w an ancestor of w
        */
    {
        embed_graph[v].visited = mark;
        embed_graph[w].visited = mark;
        embedg_VES_add_edge(embed_graph, n, edge_pos, v, w,
                                       TRUE, mark);
        return;
    }

    /*
      else there is a tree path w to d_w and an
      unembedded back edge (v, d_w)

      get the last elt in w's bicomp list
    */
    bicomp_list = embed_graph[w].pertinent_bicomp_list;
    ASSERT(!embedg_dlcl_is_empty(bicomp_list));
    vw = (embedg_dlcl_list_last(bicomp_list))->info;
    c = vw - n;

    /*
      search for the least neighbour of v >= c,
      that is in the subtree rooted by c
    */
    d_w = embedg_get_least_neigh(dfs_tree, back_edges, n, v, c);
    ASSERT(d_w >= c);
    /*
      this must be true since v is incident to a descendant of w
      (remember: w is pertinent)
    */

    /*
      mark the DFS tree path from d_w to w
    */
    embedg_mark_tree_path(embed_graph, n, d_w, w, mark);
    /*
      add the unembedded (d_w, v) edge
    */
    embedg_VES_add_edge(embed_graph, n, edge_pos, d_w, v,
                                   TRUE, mark);
}


static void 
embedg_add_mark_v_w_for_B (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v, int w,
        int *u_z, int mark)
    /*
      marking a Kuratowski homeomorph:
      
      marking and adding the unembedded dotted edge (v, w) for minor B:
      w is pertinent wrt v
    */
{
    int          vz, z, d_z, d_w;
    t_dlcl       *bicomp_list;
    
    /*
      get the last elt in w's bicomp list
    */
    bicomp_list = embed_graph[w].pertinent_bicomp_list;
    ASSERT(!embedg_dlcl_is_empty(bicomp_list));
    vz = (embedg_dlcl_list_last(bicomp_list))->info;
    z = vz - n;

    /*
      get the lowpoint of z
    */
    *u_z = embed_graph[z].lowpoint;
 
    /*
      search for the least neighbour of *u_z  >= z,
      that is in the subtree rooted by c
    */
    d_z = embedg_get_least_neigh(dfs_tree, back_edges, n, *u_z, z);
    ASSERT(d_z >= z);
    /*
      this must be true since u_z is incident to z or a descendant of z
    */

    /*
      now do the same for neighbours of v
    */
    d_w = embedg_get_least_neigh(dfs_tree, back_edges, n, v, z);
    ASSERT(d_w >= z);
    /*
      this must be true since v is incident to a descendant of w
      (remember: w is pertinent)
    */
  
    /*
      mark the DFS tree path from d_w to w
    */
    embedg_mark_tree_path(embed_graph, n, d_w, w, mark);
    /*
      mark the DFS tree path from d_z to z
    */
    embedg_mark_tree_path(embed_graph, n, d_z, z, mark);
    /*
      add & mark the edges (u_z, d_z), (v, d_w)
    */
    embedg_VES_add_edge(embed_graph, n, edge_pos, *u_z, d_z,
                                   TRUE, mark);
    embedg_VES_add_edge(embed_graph, n, edge_pos, v, d_w,
                                   TRUE, mark);
}

static void 
embedg_mark_x_y_path (t_ver_edge *embed_graph, int n, int *path_v,
        int *path_e, int nbr_v, int mark)
{
    int          i;

    /*      
      have a look at embedg_iso_get_highest_x_y_path
      to see that path_e[0] is a dummy

      (note: path_v and path_e contain nbr_v + 1 elts!)
    */
    embed_graph[path_v[0]].visited = mark;
    for (i = 1; i <= nbr_v; i++)
    {
        int        e, twin;
        
        embed_graph[path_v[i]].visited = mark;
        e = path_e[i];
        twin = embedg_VES_get_twin_edge(embed_graph, n, e);
        embed_graph[e].visited =
            embed_graph[twin].visited = mark;
    }
}

void 
embedg_mark_minor_A (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v, int c, int vr)
{
    int          r, r_c, x, y, w, u_x, u_y, u;

    ASSERT(embedg_VES_is_virtual_vertex(n, vr));
    r_c = vr - n;
    r = embed_graph[r_c].DFS_parent;

    /*
      find the ext. active x & y, and the pertinent w,
      and mark the external face of the bicomp rooted at vr
    */
    embedg_iso_get_x_y_w(embed_graph, n, v, r, r_c,
                              MARK_MINORS(n),
                              MARK_MINORS(n), MARK_MINORS(n), &x, &y, &w);

    /*
      mark the edges (u, x), (u, y), (v, w)
    */
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, x, &u_x,
                        MARK_MINORS(n));
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, y, &u_y,
                        MARK_MINORS(n));
    embedg_add_mark_v_w(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, w,
                        MARK_MINORS(n));

    /*
      mark the tree path from r to min(u_x, u_y)
    */
    u = u_x <= u_y ? u_x : u_y;
    embedg_mark_tree_path(embed_graph, n, r, u, MARK_MINORS(n));

    IF_DEB(
           fprintf(stdout, "mark minor A\n");
           fprintf(stdout, "v %d\t c %d\t r %d\t r_c %d\t x %d\t y %d\t w %d\t u_x %d\t u_y %d\n",
                   v, c, r, r_c, x, y, w, u_x, u_y);
           )
}

void 
embedg_mark_minor_B (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v,
        int c, int x, int y, int w)
{
    int          vv, u_x, u_y, vz, u_z, u_max, u_min;

    vv = c + n;

    /*
      mark the external face of the bicomp rooted by v^c
    */
    embedg_VES_walk_mark_ext_face(embed_graph, n, vv, MARK_MINORS(n));
    ASSERT(embedg_VES_is_ext_face_marked(embed_graph, n, vv,
                                              MARK_MINORS(n)));

    /*
      mark the edges (u, x), (u, y)
    */
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, x, &u_x,
                        MARK_MINORS(n));
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, y, &u_y,
                        MARK_MINORS(n));

    /*
      mark the dotted edges (v, w), (v, u)
    */
    embedg_add_mark_v_w_for_B(dfs_tree, back_edges,
                              embed_graph, n, edge_pos, v, w,
                              &u_z, MARK_MINORS(n));

    /*
      mark the tree path from max(u_x, u_y, u_z) to min(u_x, u_y, u_z)
    */
    u_max = u_x > u_y ? u_x : u_y;
    u_max = u_max > u_z ? u_max : u_z;
    u_min = u_x < u_y ? u_x : u_y;
    u_min = u_min < u_z ? u_min : u_z;
    embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));

    IF_DEB(
           fprintf(stdout, "mark minor B\n");
           fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t u_x %d\t u_y %d\t u_z %d\n",
                   v, c, x, y, w, u_x, u_y, u_z);
           )
}

void 
embedg_mark_minor_C (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v,
        int c, int x, int y, int w, int *path_v, int *path_e,
        int nbr_v, boolean px_attached_high, boolean py_attached_high)
{
    int          vv, p_x, p_y, u_x, u_y, u;

    vv = c + n;
    p_x = path_v[0];
    p_y = path_v[nbr_v];
    /*
      see embedg_iso_get_highest_x_y_path for the above
    */
    
    if (px_attached_high)
        /*
          mark the external face:
          - from v^c to p_y if py_attached_high
          - from v^c to y if !py_attached_high

          not too sure about that one....

          from v^c to p_y: so vvin = 0,
          in x's direction
        */
    {
        if (py_attached_high)
            embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                    vv, p_y, MARK_MINORS(n));
        else
            embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                    vv, y, MARK_MINORS(n));
    }
    else
        /*
          symmetric case:
          mark the external face from v^c to p_x: so vvin = 1,
          in y's direction
        */
    {
        if (px_attached_high)
            embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                    vv, p_x, MARK_MINORS(n));
        else
            embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                    vv, x, MARK_MINORS(n));
    }

    /*
      mark the edges (u, x), (u, y), (v, w)
    */
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, x, &u_x,
                        MARK_MINORS(n));
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, y, &u_y,
                        MARK_MINORS(n));
    embedg_add_mark_v_w(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, w,
                        MARK_MINORS(n));
 
    /*
      mark the tree path from v to min(u_x, u_y)
    */
    u = u_x <= u_y ? u_x : u_y;
    embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

    /*
      finally, mark the x-y path, ie the vertices in path_v
      and the edges in path_e
    */
    embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
                         MARK_MINORS(n));

    IF_DEB(
           fprintf(stdout, "mark minor C      p_x high %d\t p_y high %d\n",
                   px_attached_high, py_attached_high);
           fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\n",
                   v, c, x, y, w, p_x, p_y, u_x, u_y);
           )
}

void 
embedg_mark_minor_D (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v,
        int c, int x, int y, int w, int *path_v, int *path_e,
        int nbr_v, int entry_in_path_e)
{
    int          i, vv, p_x, p_y, u_x, u_y, u;

    vv = c + n;
    p_x = path_v[0];
    p_y = path_v[nbr_v];
    /*
      see embedg_iso_get_highest_x_y_path for the above
    */
    
    /*
      mark the lower external face from x to y: we can walk in
      either direction
    */
    embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                            x, y, MARK_MINORS(n));

    /*
      mark the internal path which goes from the x-y path to v
      - since I haven't stored those vertices/edges I assume
      that a proper face walk should suffice

      BUT a walk that say starts at p_x and ends at vv,
      that is, a walk starting at path_e[1] entered from entry_in_path_e
      (recall that path_e[0] is a dummy)
    */
    embedg_VES_walk_mark_part_proper_face(embed_graph, n,
                                               path_e[1], entry_in_path_e,
                                               vv, MARK_MINORS(n));

    /*
      a note of caution here:
      ALWAYS mark external/internal faces before adding any other edges:
      since adding edges destroys the faces' consistency
      (adding edges makes no sense of face since we are in a non-planar
      situation)
    */
    /*
      mark the edges (u, x), (u, y), (v, w)
    */
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, x, &u_x,
                        MARK_MINORS(n));
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, y, &u_y,
                        MARK_MINORS(n));
    embedg_add_mark_v_w(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, w,
                        MARK_MINORS(n));
 
    /*
      mark the tree path from v to min(u_x, u_y)
    */
    u = u_x <= u_y ? u_x : u_y;
    embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

    /*
      mark the x-y path, ie the vertices in path_v
      and the edges in path_e
    */
    embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
                         MARK_MINORS(n));

    IF_DEB(
           fprintf(stdout, "mark minor D\n");
           fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\n",
                   v, c, x, y, w, p_x, p_y, u_x, u_y);
           )
}




minor 
embedg_mark_minor_E (t_dlcl **dfs_tree, t_dlcl **back_edges,
        t_ver_edge *embed_graph, int n, int *edge_pos, int v,
        int c, int x, int y, int w, int *path_v, int *path_e, int nbr_v)
    /*
      while marking minor E return which of the minors we are dealing with
    */
{
    int          vv, p_x, p_y, u_x, u_y, u_w, u, u_max, u_min;

    vv = c + n;
    p_x = path_v[0];
    p_y = path_v[nbr_v];
    /*
      see embedg_iso_get_highest_x_y_path for the above
    */

    if (!embedg_VES_is_ver_ext_active(embed_graph, n, v, w))
        /*
          minor E1 case: we must find an ext. active z, distinct from w,
          on the external face p_x..w..p_y
        */
    {
        int       s, sin, cur, curin, z, u_z, u_xy;
        
        s = n;
        /*
          start searching at vv entered from 0 (in x's direction)
          -- we MUST reach p_x - hopefully! :)
        */
        cur = vv;
        curin = 0;
        while (s != p_x)
            /*
              first advance to p_x: we are sure of reaching it
            */
        {
            embedg_VES_get_succ_on_ext_face(embed_graph, n,
                                                 cur, curin,
                                                 FALSE, 0, &s, &sin);
            cur = s;
            curin = sin;
        }

        /*
          continue the walk on the external face:
          stop if either s is ext. active OR s == w

          we'll mark the lot later on
        */
        while (
               !(embedg_VES_is_ver_ext_active(embed_graph, n, v,
                                                         s)
                 && s != p_x)
               && s != w)
        {
            embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
                                                 FALSE, 0, &s, &sin);
            cur = s;
            curin = sin;
        }
        /*
          now we must decide which symmetry we are in
        */
        if (embedg_VES_is_ver_ext_active(embed_graph, n, v, s))
            /*
              z is between x and w (recall that w is NOT ext. active)
            */
        {
            z = s;
            ASSERT(z != w);

            /*
              mark the external face from v^c to y in x's direction
            */
            embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                    vv, y, MARK_MINORS(n));
            /*
              add/mark dotted edge (u, y)
            */
            embedg_add_mark_u_x(dfs_tree, back_edges,
                                embed_graph, n, edge_pos,
                                v, y, &u_xy, MARK_MINORS(n));
        }
        else
            /*
              this is the symmetric case: must find z between w and p_y
            */
        {
            ASSERT(s == w);
            embedg_VES_get_succ_ext_active_on_ext_face(embed_graph, n, 
                                                            v, cur, curin,
                                                            FALSE, 0,
                                                            &s, &sin);
            /*
              and z is distinct from p_y!
            */
            z = s;
            ASSERT(z != p_y);

            /*
              mark the external face from v^c to x in y's direction
            */
            embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                    vv, x, MARK_MINORS(n));
            /*
              add/mark dotted edge (u, x)
            */
            embedg_add_mark_u_x(dfs_tree, back_edges,
                                embed_graph, n, edge_pos,
                                v, x, &u_xy, MARK_MINORS(n));
        }
        /*
          now the marked bits which are common to both cases:
          dotted edges (u, z), (v, w), the x-y path,
          the tree path (v, min(u_xy, u_z))
        */
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, z, &u_z, MARK_MINORS(n));
        embedg_add_mark_v_w(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, w,
                            MARK_MINORS(n));

        embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
                             MARK_MINORS(n));
        
        u = u_z <= u_xy ? u_z : u_xy;
        embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

        IF_DEB(
               fprintf(stdout, "mark minor E1\n");
               fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t z %d\t w %d\t p_x %d\t p_y %d\t u_xy %d\t u_z %d\n",
                       v, c, x, y, z, w, p_x, p_y, u_xy, u_z);
           )

        return MINOR_E1;
    }
    
    /*
      in all other cases we get u_x, u_y, u_w back
      from the ext. active vertices x, y, w resp.

      again, I CANNOT embed these edges now since that would destroy
      my external/internal faces
    */
    
    embedg_get_u_x(embed_graph, n, v, x, &u_x);
    embedg_get_u_x(embed_graph, n, v, y, &u_y);
    embedg_get_u_x(embed_graph, n, v, w, &u_w);

    if (u_w > u_x && u_w > u_y)
        /*
          minor E2 case:
          we mark the whole external face rooted by v^c
          and the tree path (v, min(u_x, u_y))
        */
    {
        embedg_VES_walk_mark_ext_face(embed_graph, n, vv,
                                           MARK_MINORS(n));
        u = u_x <= u_y ? u_x : u_y;
        embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

        /*
          embed dotted edges (u, x), (u, y) & (u, w)
        */
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, x, &u_x, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, y, &u_y, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, w, &u_w, MARK_MINORS(n));
        
        IF_DEB(
               fprintf(stdout, "mark minor E2\n");
               fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
                       v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
           )
            
        return MINOR_E2;
    }

    /*
      two more things common to all remaining cases:

      mark the dotted edge (v, w) (but we MUST do that later)
      
      mark the x-y path
    */
    embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
                         MARK_MINORS(n));
     
    if (u_x < u_y && u_w < u_y)
        /*
          minor E3 case: one of the symmetric cases:
          the external face rooted at v_c from vv to x (in x's direction)
          the external face rooted at v_c from y to w (in y's direction)
          the (v, min(u_w, u_x)) tree path
        */
    {
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                vv, p_x, MARK_MINORS(n));
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                y, w, MARK_MINORS(n));
        
        u = u_x <= u_w ? u_x : u_w;
        embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

        /*
          embed dotted edges (u, x), (u, y), (u, w), (v, w)
        */
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, x, &u_x, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, y, &u_y, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, w, &u_w, MARK_MINORS(n));
        embedg_add_mark_v_w(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, w,
                            MARK_MINORS(n));

        IF_DEB(
               fprintf(stdout, "mark minor E3/a\n");
               fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
                       v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
           )
            
        return MINOR_E3;
    }
    if (u_y < u_x && u_w < u_x)
        /*
          minor E3 case: the other symmetric case:
          the external face rooted at v_c from vv to y (in y's direction)
          the external face rooted at v_c from x to w (in x's direction)
          the (v, min(u_w, u_y)) tree path
        */
    {
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                vv, p_y, MARK_MINORS(n));
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                x, w, MARK_MINORS(n));

        u = u_y <= u_w ? u_y : u_w;
        embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

        /*
          embed dotted edges (u, x), (u, y), (u, w), (v, w)
        */
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, x, &u_x, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, y, &u_y, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, w, &u_w, MARK_MINORS(n));
        embedg_add_mark_v_w(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, w,
                            MARK_MINORS(n));

        IF_DEB(
               fprintf(stdout, "mark minor E3/b\n");
               fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
                       v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
           )
            
        return MINOR_E3;
    }
    
    if (p_x != x)
        /*
          minor E4 case: one of the symmetric cases:
          the external face rooted at v_c from vv to w (in x's direction)
          the external face rooted at v_c from vv to p_y (in y's direction)
          the tree path from max(u_x, u_y, u_w) to min(u_x, u_y, u_w)
        */
    {
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                vv, w, MARK_MINORS(n));
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                vv, p_y, MARK_MINORS(n));
        
        u_max = u_x > u_y ? u_x : u_y;
        u_max = u_max > u_w ? u_max : u_w;
        u_min = u_x < u_y ? u_x : u_y;
        u_min = u_min < u_w ? u_min : u_w;
        embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));

        /*
          embed dotted edges (u, x), (u, y), (u, w), (v, w)
        */
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, x, &u_x, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, y, &u_y, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, w, &u_w, MARK_MINORS(n));
        embedg_add_mark_v_w(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, w,
                            MARK_MINORS(n));

        IF_DEB(
               fprintf(stdout, "mark minor E4/a\n");
               fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
                       v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
           )
            
        return MINOR_E4;
    }
    if (p_y != y)
        /*
          minor E4 case: the other symmetric case:
          the external face rooted at v_c from vv to w (in y's direction)
          the external face rooted at v_c from vv to x (in x's direction)
          (here p_x = x!)
          the tree path from max(u_x, u_y, u_w) to min(u_x, u_y, u_w)
        */
    {
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
                                                vv, w, MARK_MINORS(n));
        embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
                                                vv, x, MARK_MINORS(n));
        
        u_max = u_x > u_y ? u_x : u_y;
        u_max = u_max > u_w ? u_max : u_w;
        u_min = u_x < u_y ? u_x : u_y;
        u_min = u_min < u_w ? u_min : u_w;
        embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));

        /*
          embed dotted edges (u, x), (u, y), (u, w), (v, w)
        */
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, x, &u_x, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, y, &u_y, MARK_MINORS(n));
        embedg_add_mark_u_x(dfs_tree, back_edges,
                            embed_graph, n, edge_pos,
                            v, w, &u_w, MARK_MINORS(n));
        embedg_add_mark_v_w(dfs_tree, back_edges,
                            embed_graph, n, edge_pos, v, w,
                            MARK_MINORS(n));

        IF_DEB(
               fprintf(stdout, "mark minor E$/b\n");
               fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
                       v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
           )
            
        return MINOR_E4;
    }

    /*
      this is the last case for minor E: when the homeomorph is K5
      
      mark the whole external face rooted at v^c
      mark the tree path from v to min(u_x, u_y, u_w)
    */

    embedg_VES_walk_mark_ext_face(embed_graph, n, vv, MARK_MINORS(n));
    
    u = u_x < u_y ? u_x : u_y;
    u = u < u_w ? u : u_w;
    embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));

    /*
      embed dotted edges (u, x), (u, y), (u, w), (v, w)
    */
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos,
                        v, x, &u_x, MARK_MINORS(n));
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos,
                        v, y, &u_y, MARK_MINORS(n));
    embedg_add_mark_u_x(dfs_tree, back_edges,
                        embed_graph, n, edge_pos,
                        v, w, &u_w, MARK_MINORS(n));
    embedg_add_mark_v_w(dfs_tree, back_edges,
                        embed_graph, n, edge_pos, v, w,
                        MARK_MINORS(n));
    
    IF_DEB(
           fprintf(stdout, "mark minor E5\n");
           fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
                   v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
           )
            
    return MINOR_E5;
}
/*
 *  proper_face_walk.c
 */
 
/*
  What:
  *****
  
  Implementing a proper face walk within the VES structure.
  This is obviously not the same as an external face walk,
  but is simply the standard face walk in a planar embedding.

  Not much to say, if only to emphasize that for our
  purposes here we assume:

  1. the short-cut edges have been removed from the VES structure
  2. each vertex/edge has been given its orientation
  2. the adjacency lists (vertex + plus its incident edges)
     are consistent:  (and this is IMPORTANT)
     that is, the way to traverse an adj. list (ie what
     constitute previous and next in the list which actually
     is a planar embedding at this stage) is indicated
     by the vertex/edge's orientation


     try to explain this better another time.... sorry...
  


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  from 

  Simplified O(n) Planarity Algorithms  (draft)
  ************************************
  
  John Boyer      JBoyer@PureEdge.com, jboyer@acm.org
  Wendy Myrvold   wendym@csr.uvic.ca


  ++++++++++++++++++++++++++++++++++++++++++++++++++++++
  authors:
  ********

  Paulette Lieby (Magma), Brendan McKay (ANU)

  Started October 2001
*/

 
#include "planarity.h"
 
#define IF_DEB(x)    {}
#define IF_DEB_PROPER_FACE(x)    {}
#define IF_VERB(x)   {}

 
 
/* aproto: header embed_graph_protos.h */




boolean 
embedg_VES_get_succ_on_proper_face_with_avoidance (t_ver_edge *embed_graph,
        int n, int e, int ein, int a, boolean MARK, int mark, int *s,
        int *next_e, int *next_ein)
    /*
      find the successor s of embed_graph[e].neighbour
      (entered via ein) on a proper face traversal
      which avoids (the vertex) a if a != n 

      also returns the edge next_e such that
      embed_graph[next_e].neighbour = s (to allow for continuation
      of the walk)

      assumes that short-cut edges have been removed and that each
      edge/vertex has been given its orientation

      and (more importantly) assumes that adjacency lists are consistent

      this function has been written especially to retrieve the highest
      x-y path for the isolator;
      (see embedg_iso_get_highest_x_y_path)
      but as I discovered later (when marking an internal face
      as for minor D) this function is general purpose

      PLUS: return true if the proper face walk has to skip an edge
      incident to a (ie had to "avoid" a)

      PLUS: mark s & next_e if so requested
    */
{
    int            eout;
    int            twin, twinout;
    boolean        avoid_a;

    ASSERT(embedg_VES_is_edge(n, e));
    ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));

    IF_DEB(
           fprintf(stdout, "get_succ_on_proper_face, \n");
           )

    avoid_a = FALSE;
    /*
      find the direction out of the edge
    */
    eout = 1 ^ ein;

    /*
      get the twin edge
    */
    twin = embedg_VES_get_twin_edge(embed_graph, n, e);
 
    /*
      for each edge we must set the way to get to the next
      in the adjacency list:
      adjacency lists are traversed according to the vertex/edges
      orientation (one unique orientation per list of course)
    */
    if (embed_graph[e].sign != embed_graph[twin].sign)
        /*
          invert traversal
        */
    {
        twinout = 1 ^ eout;
    }
    else
        /*
          traversal is identical
        */
    {
        twinout = eout;
    }
 
    /*
      now, we want the edge previous to twin in twin's adjacency list,
      ie link[1 ^ twinout]
    */
    *next_e = embed_graph[twin].link[1 ^ twinout];
    /*
      next_e could be a vertex, I need an edge
    */
    if (embedg_VES_is_vertex(n, *next_e)
        || embedg_VES_is_virtual_vertex(n, *next_e))
        /*
          at this stage all virtual vertices should have
          been disabled BUT the vertices rooting the bicomps!!!
        */
    {
        *next_e = embed_graph[*next_e].link[1 ^ twinout];
    }
    ASSERT(embedg_VES_is_edge(n, *next_e));
    ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
    *s = embed_graph[*next_e].neighbour;

    if (*s == a)
        /*
          want to avoid this vertex, so must get yet previous
          edge in adjacency list
        */
    {
        avoid_a = TRUE;
        
        *next_e = embed_graph[*next_e].link[1 ^ twinout];
        if (embedg_VES_is_vertex(n, *next_e)
            || embedg_VES_is_virtual_vertex(n, *next_e))
        {
            *next_e = embed_graph[*next_e].link[1 ^ twinout];
        }
        ASSERT(embedg_VES_is_edge(n, *next_e));
        ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
    }
    *s = embed_graph[*next_e].neighbour;
    ASSERT(*s != a);

    /*
      finally (again, because lists  are consistent)
    */
    *next_ein = 1 ^ twinout;

    /*
      now mark s and next_e if required
    */
    if (MARK)
    {
        embed_graph[*s].visited = 
            embed_graph[*next_e].visited = mark;
        /*
          ouuh... must mark the twin as well....
          but ONLY when we mark the minors....
          that is poor design, can we do better????
          -- don't think so...

          (when we mark when counting the faces, we MUST only
          mark the edge and NOT its twin)
        */
        if (mark == MARK_MINORS(n))
        {
            twin =
                embedg_VES_get_twin_edge(embed_graph, n, *next_e);
            embed_graph[twin].visited = mark;
        }
    }

    return avoid_a;
}



void 
embedg_VES_get_succ_on_proper_face (t_ver_edge *embed_graph, int n, int e,
        int ein, int MARK, int mark, int *s, int *next_e, int *next_ein)
    /*
      same as above but without avoidance
    */
{
    boolean  avoid;
    
    avoid =
        embedg_VES_get_succ_on_proper_face_with_avoidance(
                                                       embed_graph, n,
                                                       e, ein, n,
                                                       MARK, mark,
                                                       s, next_e, next_ein);
    ASSERT(avoid == FALSE);
}


void 
embedg_VES_walk_proper_face (t_ver_edge *embed_graph, int n, int e,
        int ein, boolean MARK, int mark)
    /*
      traversing a proper face starting at edge e which has been entered
      via ein

      -- we mark the visited edges with mark if so requested

      assumes that short-cut edges have been removed and that each
      edge/vertex has been given its orientation
    */
{
    int     s, cur_e, cur_ein, next_e, next_ein;

    next_e = n; /* this is an invalid value for an edge */

    IF_DEB_PROPER_FACE(
                       fprintf(stdout, "proper face traversal\n");
                       )

    cur_e = e;
    cur_ein = ein;
    while (next_e != e)
    {
        ASSERT(embedg_VES_is_edge(n, cur_e));
        ASSERT(!embedg_VES_is_short_cut_edge(embed_graph,
                                                        n, cur_e));
        IF_DEB_PROPER_FACE(
                           embedg_VES_print_edge(embed_graph, n, cur_e);
                           )

        embedg_VES_get_succ_on_proper_face(embed_graph, n,
                                                cur_e, cur_ein,
                                                MARK, mark,
                                                &s, &next_e, &next_ein);
        cur_e = next_e;
        cur_ein = next_ein;
    }

    /*
      note that by doing so we would have marked e and the first of e's
      endpoints since by exiting the loop e = next_e and s is the
      actual starting vertex of the walk
    */
}





