/* 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 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 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 */ { 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 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 */ }