planar.w
上传用户:gzelex
上传日期:2007-01-07
资源大小:707k
文件大小:53k
开发平台:

MultiPlatform

  1. documentstyle[comments,a4,psfig]{cweb}
  2. typeout{TransFig: figure text in LaTeX.}
  3. typeout{TransFig: figures in PostScript.}
  4. begingroupmakeatletter
  5. % extract first six characters in fmtname
  6. defx#1#2#3#4#5#6#7relax{defx{#1#2#3#4#5#6}}
  7. expandafterxfmtname xxxxxxrelax defy{splain}
  8. endgroup
  9. endinput
  10. voffset=-0.5cm
  11. textwidth 14cm
  12. oddsidemargin          0.4cm
  13. evensidemargin         1.4cm
  14. marginparwidth 1.9cm
  15. marginparsep 0.4cm
  16. marginparpush 0.4cm
  17. topmargin 0cm
  18. headsep 1.5cm
  19. textheight 21.5cm
  20. footskip 2.2cm
  21. begin{document}
  22. @i ../leda_types.w
  23. thispagestyle{empty}
  24. renewcommand{thefootnote}{fnsymbol{footnote}}
  25. title{An Implementation of the Hopcroft and Tarjan Planarity Test and Embedding Algorithm}
  26. thanks{This work was supported in part by the German Ministry for Research 
  27. and Technology (Bundesministerium fuer Forschung und Technologie) under 
  28. grant ITS 9103 and by the ESPRIT Basic Research Actions Program under 
  29. contract No. 7141 (project ALCOM II).}
  30. author{Kurt Mehlhorn\
  31.         {footnotesize  Max-Planck-Institut fuer Informatik,}\[-0.7ex]
  32.     {footnotesize 66123 Saarbruecken, Germany}\[0.7ex]
  33. and Petra Mutzel\
  34.         {footnotesize  Institut fuer Informatik,}\[-0.7ex]
  35. {footnotesize Universitaet zu Koeln, 50969 Koeln}\[0.7ex]
  36. and Stefan Naeher\
  37.         {footnotesize  Max-Planck-Institut fuer Informatik,}\[-0.7ex]
  38.     {footnotesize 66123 Saarbruecken, Germany}\[0.7ex]}
  39.  
  40.         date{} 
  41.         maketitle
  42. setcounter{page}{0}
  43. thispagestyle{empty}
  44. %%%%%%% Abstract %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  45. vspace*{2.2cm}
  46. begin{abstract}
  47. We describe an implementation of the Hopcroft and Tarjan planarity test and
  48. embedding algorithm. The program tests the planarity of the input
  49. graph and either constructs a combinatorial embedding (if the graph
  50. is planar) or exhibits a Kuratowski subgraph (if the graph is non-planar).
  51. end{abstract}
  52. tableofcontents
  53. @* Introduction.
  54. We descibe two procedures to test the planarity of a graph |G|:
  55.  
  56. begin{center}
  57. |bool planar (graph& G, bool embed = false)|
  58. end{center}
  59. and
  60. begin{center}
  61. |bool planar (graph& G, list <edge>& P, bool embed = false)|.
  62. end{center}
  63. Both take a directed graph |G| and test it for planarity.
  64. If the graph is planar and bidirected, i.e., for every edge of |G| its
  65. reversal is also in |G|, and the argument |embed| is true, then they
  66. also compute a combinatorial embedding of |G| (by suitably reordering
  67. its adjacency lists). If the graph |G| is
  68. non-planar then the first version of |planar| only records that fact.
  69. The second version in addition returns a subgraph of |G| homeomorphic
  70. to $K_{3,3}$ or $K_5$ (as a list |P| of edges). For a planar graph
  71. |G| the running time of
  72. both versions is linear (cf. section ref{Efficiency} for more
  73. detailed information). For non-planar graphs |G| the
  74. first version runs in linear time but the second version runs in 
  75. quadratic time.
  76. We are aware of the linear time algorithm of Williamson
  77. cite{Williamson:Kuratowski} to find Kuratowski subgraphs but have
  78. not implemented it. 
  79. The implementation of |planar| is based on the LEDA platform of combinatorial
  80. and geometric computing cite{LEDA-Manual,Mehlhorn-Naeher:LEDA}. It is part of
  81. the LEDA-distribution (available through anonymous ftp at cs.uni-sb.de). In
  82. this document we describe the implementation of both versions of |planar| and 
  83. a demo, and report on our experimental experience.
  84. Procedure |planar| is based on the
  85. Hopcroft and Tarjan linear time planarity testing algorithm as 
  86. described in cite[section IV.10]{Me:book}. For the sequel we assume
  87. knowledge of section IV.10 of cite{Me:book}. A revised version of that section
  88. is included in this document (see section ref{reprint}) for the convenience
  89. of the reader. Our procedure |planar| differs from cite[section IV.10]{Me:book}
  90. in two respects: Firstly, it works for arbitrary directed graphs and 
  91. not only for biconnected
  92. undirected graphs. To this end we augment the input graph by additional edges
  93. to make it biconnected and bidirected. The augmentation does not destroy 
  94. planarity. Secondly, the embedding 
  95. phase follows the
  96. presentation in cite{Mehlhorn-Mutzel:embedding}. We want to remark that the
  97. description of the embedding phase given 
  98. in cite[section IV.10]{Me:book} is false.
  99. The essential part of cite{Mehlhorn-Mutzel:embedding} is reprinted in
  100. section ref{embedding}.
  101. This document defines the files |planar.h|, |planar.c|, and |demo.c|.
  102. |planar.c| contains the code for procedure |planar|, |demo.c|
  103. contains the code for a demo, and |planar.h| consists of the 
  104. declarations of procedure |planar|. 
  105. The third file is defined in section ref{demo}, the structure of the first two files
  106. is as follows:
  107. @(planar.h@>=
  108. bool planar (graph& G, bool embed = false);
  109. bool planar (graph& G, list <edge>& P, bool embed = false);
  110. void  Make_biconnected_graph(graph& G);
  111. @
  112. @(planar.c@>=
  113. @<includes@>;
  114. @<typedefs, global variables and class declarations@>;
  115. @<auxiliary functions@>;
  116. @<first version of |planar|@>;
  117. @<second version of |planar|@>;
  118. @ We include parts of LEDA (who would want to 
  119. work without it) cite{LEDA-Manual,Mehlhorn-Naeher:LEDA}.
  120. We need stacks, graphs, and graph algorithms.
  121. @<includes@>=
  122. #include <LEDA/stack.h>
  123. #include <LEDA/graph.h>
  124. #include <LEDA/graph_alg.h>
  125. #include "planar.h"
  126. @ The second version of |planar| is easy to describe. We first test the
  127. planarity of |G| using the first version.
  128. If |G| is planar then we are done. If |G| is 
  129. non-planar then we cycle through the edges of |G|. For every edge |e| of 
  130. |G| we test the planarity of |G-e|. If |G-e| is planar we add |e| back in. 
  131. In this way we determine a minimal (with respect to set inclusion)
  132. non-planar subgraph of |G|, i.e., either a $K_5$ or a $K_{3,3}$.
  133. @<second version...@>=
  134. bool planar (graph& G, list <edge>& P, bool embed = false)
  135. if (planar(G, embed)) return true;
  136. /* We work on a copy |H| of |G| since the procedure alters |G|; we link every 
  137. vertex and edge of |H| with its original. For the vertices we also have the
  138. reverse links. */
  139. GRAPH<node,edge> H; 
  140. node_array<node> link(G);
  141. node v;
  142. forall_nodes(v,G) link[v] = H.new_node(v);
  143. /* This requires some explanation. |H.new_node(v)| adds a new node to
  144. |H|, returns the new node, and makes |v| the information associated
  145. with the new node. So the statement creates a copy of |v| and
  146. bidirectionally links it with |v| */
  147. edge e;
  148. forall_edges(e,G) H.new_edge(link[source(e)],link[target(e)],e);
  149. /* |link[source(e)]| and |link[target(e)]| are the copies of |source(e)|
  150. and |target(e)| in |H|. The operation |H.new_edge| creates a new edge
  151. with these endpoints, returns it, and makes |e| the information of that
  152. edge. So the effect of the loop is to make the edge set of |H| a copy
  153. of the edge set of |G| and to let every edge of |H| know its original.
  154. We can now determine a minimal non-planar subgraph of |H| */
  155. list<edge> L = H.all_edges();
  156. edge eh;
  157. forall(eh,L)
  158.    {e = H[eh]; // the edge in |G| corresponding to |eh|
  159.     node x = source(eh); node y = target(eh);
  160.     H.del_edge(eh);
  161.     if (planar(H)) 
  162.        H.new_edge(x,y,e);  //put a new version of |eh| back in and establish the correspondence
  163.       
  164.     }
  165. /* |H| is now a homeomorph of either $K_5$ or $K_{3,3}$. We still need 
  166. to translate back to |G|. */
  167. P.clear();
  168. forall_edges(eh,H) P.append(H[eh]);
  169. return false;
  170. }
  171. @ The first version of |planar| is also quite simple to describe.
  172. Graphs with at most three
  173. vertices are always planar. So assume that |G| has more than three
  174. vertices. We first add edges to |G| to make it bidirected
  175. and then add some more edges to make
  176. it biconnected (of course, without destroying planarity). Then we test
  177. the planarity of the extended graph and construct an embedding. 
  178. Since |planar| alters the input graph, it works on a copy of it.
  179. @<first version ...@>=
  180. bool planar(graph& Gin, bool embed = false)
  181. /* |Gin| is a directed graph. |planar| decides whether |Gin| is planar.
  182. If it is and |embed == true| then it also computes a 
  183.  combinatorial embedding of |Gin| by suitably reordering 
  184. its adjacency lists.
  185. |Gin| must be bidirected in that case. */
  186. {int n = Gin.number_of_nodes();
  187. if (n <= 3) return true;
  188. if (Gin.number_of_edges() > 6*n - 12) return false;
  189. /* An undirected planar graph has at most $3n-6$ edges; a directed graph may
  190. have twice as many */
  191. cout << "number of nodes and edges  " << n << "  " << Gin.number_of_edges();
  192. newline;
  193. float T = used_time();
  194. @<make |G| a copy of |Gin| and add edges to make |G| bidirected@>;
  195. @<make |G| biconnected@>;
  196. cout << "time to copy and to make bidirected and connected  " << used_time(T);
  197. newline;
  198. @<test planarity@>;
  199. cout << "time to test planarity   " << used_time(T);
  200. newline;
  201. if (embed) {
  202.   if (Gin_is_bidirected) @<construct embedding@>
  203.   else error_handler(2,"sorry: can only embed bidirected graphs");
  204.   cout << "time to construct embedding   " << used_time(T);
  205.   newline;
  206. }
  207. return true;
  208. }
  209. @ We make |G| a copy of |Gin| and bidirectionally link all vertices and
  210. edges. Then we add edges to |G| to make it bidirected. In
  211. |Gin_is_bidirected| we record whether we needed to add edges.
  212. @<make |G| a copy ...@>=
  213. GRAPH<node,edge> G;
  214. edge_array<edge> companion_in_G(Gin);
  215.  
  216. node_array<node> link(Gin);
  217. bool Gin_is_bidirected = true;
  218. {node v;
  219. forall_nodes(v,Gin) link[v] = G.new_node(v); //bidirectional links
  220. edge e;
  221. forall_edges(e,Gin) companion_in_G[e] =   
  222.                       G.new_edge(link[source(e)],link[target(e)],e);
  223. }
  224. @<bidirect G@>;
  225. @ We bidirect G. We first assign numbers to nodes and edges. We make sure that
  226. the two versions of the same undirected edge get the same number but versions
  227. of distinct undirected edges get different numbers. Then we sort the edges 
  228. according to numbers. Finally we step through the sorted list of edges 
  229. and add
  230. missing edges.
  231. @<bidirect G@>= 
  232. {node_array<int> nr(G);
  233. edge_array<int> cost(G);
  234. int cur_nr = 0;
  235. int n = G.number_of_nodes();
  236. node v;
  237. edge e;
  238. forall_nodes(v,G) nr[v] = cur_nr++;
  239. forall_edges(e,G) cost[e] = ((nr[source(e)] < nr[target(e)]) ?
  240.                               n*nr[source(e)] + nr[target(e)] :
  241.                               n*nr[target(e)] + nr[source(e)]);
  242. G.sort_edges(cost);
  243. list<edge> L = G.all_edges();
  244. while (! L.empty())
  245. {e = L.pop();
  246. /* check whether the first edge on |L| is equal to the reversal of |e|. If so,
  247. delete it from |L|, if not, add the reversal to |G| */
  248. if (!L.empty() && (source(e) == target(L.head())) && (source(L.head()) == target(e)))
  249. L.pop();
  250. else {
  251.    G.new_edge(target(e),source(e));
  252.    Gin_is_bidirected = false;
  253. }
  254. }
  255. }
  256. @* Making the Graph Biconnected.
  257. We make |G| biconnected. We first make it connected by linking all
  258. roots. Assume that is done. Let $a$ be any articulation 
  259. point and let $u$ and $v$ be neighbors of $a$ belonging to different
  260. biconnected components. Then there are embeddings of the two components
  261. with the edges ${u,a}$ and ${v,a}$ on the boundary of the unbounded
  262. face. Hence we may add the edge ${u,v}$ without destroying planarity.
  263. Proceeding in this way we make |G| 
  264. biconnected.
  265. In |Make_biconnected_graph| we change the graph while working on it. But we modify only
  266. those adjacency lists that will not be touched later.
  267. We need the biconnected version of |G| (|G| will be further modified
  268. during the planarity test) in order to construct the planar embedding. So we
  269. store it as a graph |H|. For every edge of |Gin| and |G| we store a link to
  270. its copy in |H|. In addition every edge of |H| is made to know its reversal.
  271. @<make |G| biconnected@>=
  272. Make_biconnected_graph(G);
  273. @<make |H| a copy of |G|@>;
  274. @ We give the details of the procedure |Make_biconnected_graph|. We first make |G|
  275. connected by linking all roots of the DFS-forest. In a second step we make
  276. |G| biconnected. 
  277. @<auxiliary ...@>=
  278. void Make_biconnected_graph(graph& G)
  279. {
  280. node v;
  281. node_array<bool> reached(G,false);
  282. node u = G.first_node();
  283. forall_nodes(v,G)
  284.   {
  285.     if( !reached[v] )
  286.       {/* explore the connected component with root |v| */ 
  287.        DFS(G,v,reached);
  288.        if ( u != v) 
  289.          {/* link |v|'s component to the first component */
  290.           G.new_edge(u,v); G.new_edge(v,u);  
  291.          } //end if       
  292.       } // end not reached
  293.   } //end forall
  294. /* |G| is now connected. We next make it biconnected. */
  295. forall_nodes(v,G) reached[v] = false;
  296. node_array<int> dfsnum(G);
  297. node_array<node> parent(G,nil);
  298. int dfs_count = 0;
  299. node_array<int> lowpt(G);
  300. dfs_in_make_biconnected_graph(G,G.first_node(),dfs_count,reached,dfsnum,lowpt,parent);
  301. } // end |Make_biconnected_graph|
  302. @ We still have to give the procedure |dfs_in_make_biconnected_graph|. It determines
  303. articulation points and adds appropriate edges whenever it discovers one.
  304. For a proof of correctness we refer the reader to
  305. cite[section IV.6]{Me:book}.
  306. @<auxiliary ...@>+= 
  307. void dfs_in_make_biconnected_graph(graph& G, node v, int & dfs_count, 
  308.  node_array<bool>& reached, @|
  309.  node_array<int> & dfsnum, node_array<int>& lowpt, 
  310.          node_array<node>& parent)
  311. {
  312. node w;
  313. edge e;
  314.   dfsnum[v] = dfs_count ++;
  315.   lowpt[v] = dfsnum[v];
  316.   reached[v] = true;
  317.   if ( !G.first_adj_edge(v) ) return; //no children
  318.   node u = target(G.first_adj_edge(v)); //first child
  319.   forall_adj_edges(e,v)
  320.     {
  321.      w = target(e);
  322.      if( !reached[w] )
  323.         {/* e is a tree edge */
  324.          parent[w] = v;
  325.          dfs_in_make_biconnected_graph(G,w,dfs_count,reached,dfsnum,lowpt,parent);
  326.          if (lowpt[w] == dfsnum[v])
  327.          { /* |v| is an articulation point. We now add an edge. If |w|
  328. is the first child and |v| has a parent then we connect |w| and |parent[v]|,
  329. if |w| is a first child and |v| has no parent then we do nothing. If |w|
  330. is not the first child then we connect |w| to the first child. The net 
  331. effect of all of this is to link all children of an articulation 
  332. point to the first child and the first child to the parent
  333. (if it exists) */
  334.          if (w == u && parent[v]) 
  335.               {G.new_edge(w,parent[v]);
  336.                G.new_edge(parent[v],w);
  337.               } 
  338.          if ( w !=u ) {G.new_edge(u,w); G.new_edge(w,u);}
  339.          } // end if lowpt = dfsnum
  340.          lowpt[v] = Min(lowpt[v],lowpt[w]);
  341.         } //end tree edge
  342.      else lowpt[v] = Min(lowpt[v],dfsnum[w]); //non tree edge
  343.     } // end forall
  344. } //end dfs
  345. @ Because we use the function |dfs_in_make_biconnected_graph| before its declaration,
  346. let's add it to the global declarations.
  347. @<typedefs, ...@>=
  348. void dfs_in_make_biconnected_graph(graph& G, node v, int & dfs_count, 
  349.  node_array<bool>& reached, @|
  350.  node_array<int> & dfsnum, node_array<int>& lowpt, 
  351.          node_array<node>& parent);
  352. @ We make |H| a copy of |G| and create bidirectional links between the
  353. vertices and edges of |G| and |H|. 
  354. Also, each edge in |Gin| gets a link to its copy in |H| and every edge
  355. of |H| gets to know its reversal. More precisely, |H[G[v]]=v| for every
  356. node |v| of |G| and |H[G[e]]=e| for every edge |e| of |G|, and
  357. |companion_in_H[ein]| is the edge in |H| corresponding to the edge
  358. |ein| of |Gin| for every edge |ein| of |Gin|. Finally, if |e=(u,v)| is
  359. an edge of |H| then |reversal[e]=(v,u)|.
  360. @<make |H| ...@>=
  361. GRAPH<node,edge> H;
  362. edge_array<edge> companion_in_H(Gin);
  363. {node v;
  364. forall_nodes(v,G) G.assign(v,H.new_node(v));
  365. edge e;
  366. forall_edges(e,G) G.assign(e,H.new_edge(G[source(e)],G[target(e)],e));
  367. edge ein;
  368. forall_edges(ein,Gin) companion_in_H[ein] = G[companion_in_G[ein]];
  369. }
  370. edge_array<edge> reversal(H);
  371. compute_correspondence(H,reversal);
  372. @* The Planarity Test.
  373. We are now ready for the planarity test proper. We follow 
  374. cite[page 95]{Me:book}. We first compute |dfsnumber|s and |parent|s, we
  375. delete all forward edges and all reversals of tree edges, and we
  376. reorder the adjaceny lists as described in cite[page 101]{Me:book}.
  377. We then test the strong planarity.
  378. The array |alpha| is needed for the embedding process. It
  379. records the placement of the subsegments. 
  380. @<test planarity@>=
  381. node_array<int> dfsnum(G);
  382. node_array<node> parent(G,nil);
  383. reorder(G,dfsnum,parent);
  384. edge_array<int> alpha(G,0);
  385. {list<int> Att;
  386. alpha[G.first_adj_edge(G.first_node())] = left; 
  387. if (!strongly_planar(G.first_adj_edge(G.first_node()),G,Att,alpha,dfsnum,parent))
  388. return false;
  389. }
  390. @ We need two global constants |left| and |right|.
  391. @<typedefs...@>+=
  392. const int left = 1;
  393. const int right = 2;
  394. @ We give the details of the procedure |reorder|. It first performs DFS
  395. to compute |dfsnum|, |parent|, |lowpt1| and |lowpt2|, and the list |Del|
  396. of all forward edges and all reversals of tree edges.
  397. It then deletes the edges in |Del| and finally it
  398. reorders the edges.
  399. @<auxiliary ...@>+=
  400. void reorder(graph& G,node_array<int>& dfsnum, 
  401.  node_array<node>& parent)
  402. {
  403. node v;
  404. node_array<bool> reached(G,false);
  405. int dfs_count = 0;
  406. list<edge> Del;
  407. node_array<int> lowpt1(G), lowpt2(G);
  408.        
  409. dfs_in_reorder(Del,G.first_node(),dfs_count,reached,dfsnum,lowpt1,lowpt2,parent);
  410.       
  411. /* remove forward and reversals of tree edges */
  412. edge e;
  413. forall(e,Del)   G.del_edge(e);
  414. /* we now reorder adjacency lists as described in cite[page 101]{Me:book} */
  415. node w;
  416. edge_array<int> cost(G);
  417. forall_edges(e,G)
  418. {v = source(e); w = target(e);
  419. cost[e] = ((dfsnum[w] < dfsnum[v]) ?
  420.            2*dfsnum[w]  : 
  421.           ( (lowpt2[w] >= dfsnum[v]) ?
  422.              2*lowpt1[w] : 2*lowpt1[w] + 1));
  423. }
  424. G.sort_edges(cost);
  425. }
  426. @ We still have to give the procedure |dfs_in_reorder|. It's a bit long but standard.
  427. @<auxiliary ...@>+= 
  428. void dfs_in_reorder(list<edge>& Del, node v, int& dfs_count,
  429.  node_array<bool>& reached, @| 
  430.  node_array<int>& dfsnum,
  431.  node_array<int>& lowpt1, node_array<int>& lowpt2, @|
  432.  node_array<node>& parent)
  433. {
  434. node w;
  435. edge e;
  436.   dfsnum[v] =  dfs_count++;
  437.   lowpt1[v] = lowpt2[v] = dfsnum[v];
  438.   reached[v] = true;
  439.   forall_adj_edges(e,v)
  440.     {
  441.      w = target(e);
  442.      if( !reached[w] )
  443.       {/* e is a tree edge */
  444.        parent[w] = v;
  445.        dfs_in_reorder(Del,w,dfs_count,reached,dfsnum,lowpt1,lowpt2,
  446.            parent);
  447.        lowpt1[v] = Min(lowpt1[v],lowpt1[w]);
  448.       } //end tree edge
  449.      else {lowpt1[v] = Min(lowpt1[v],dfsnum[w]); // no effect for forward edges
  450.           if(( dfsnum[w] >= dfsnum[v])  || w == parent[v] ) 
  451.                /* forward edge or reversal of tree edge */
  452.                Del.append(e) ; 
  453.           } //end non-tree edge
  454.           
  455.                
  456.      } // end forall
  457.   /* we know |lowpt1[v]| at this point and now make a second pass over all
  458.      adjacent edges of |v| to compute |lowpt2| */
  459.   
  460.   forall_adj_edges(e,v)
  461.     {w = target(e);
  462.      if (parent[w] ==v)
  463.      { /* tree edge */
  464.        if (lowpt1[w] != lowpt1[v]) lowpt2[v] = Min(lowpt2[v],lowpt1[w]);
  465.        lowpt2[v] = Min(lowpt2[v],lowpt2[w]);
  466.      } //end tree edge
  467.      else // all other edges 
  468.           if (lowpt1[v] != dfsnum[w]) lowpt2[v] = Min(lowpt2[v],dfsnum[w]);
  469.      } //end forall
  470. } //end dfs
  471. @ Because we use the function |dfs_in_reorder| before its declaration,
  472. let's add it to the global declarations.
  473. @<typedefs, ...@>+=
  474. void dfs_in_reorder(list<edge>& Del, node v, int& dfs_count,
  475.  node_array<bool>& reached, @| 
  476.  node_array<int>& dfsnum,
  477.  node_array<int>& lowpt1, node_array<int>& lowpt2, @|
  478.  node_array<node>& parent);
  479. @ We now come to the heart of the planarity test: procedure |strongly_planar|.
  480. It takes a tree edge $e0 = (x,y)$ and tests whether  
  481. the segment $S(e0)$ is strongly planar. If  successful it returns  (in |Att|) the
  482. ordered list of attachments of $S(e0)$ (excluding $x$); high DFS-numbers are
  483. at the front of the list. In |alpha| it
  484. records the placement of the subsegments.
  485. |strongly_planar| operates in three phases. It first constructs the cycle $C(e0)$
  486. underlying the segment $S(e0)$. It then constructs the interlacing graph for the
  487. segments emanating from the spine of the cycle. If this graph is non-bipartite
  488. then the segment $S(e0)$ is non-planar. If it is bipartite then the segment is
  489. planar. In this case the third phase checks whether the segment is strongly 
  490. planar and, if so, computes its list of attachments.
  491. @<auxiliary ...@>+=
  492. bool strongly_planar(edge e0, graph& G, list<int>& Att, 
  493.  edge_array<int>& alpha, @|
  494.  node_array<int>& dfsnum,
  495.  node_array<node>& parent)
  496. {
  497. @<determine the cycle $C(e0)$@>;
  498. @<process all edges leaving the spine@>;
  499. @<test strong planarity and compute |Att|@>;
  500. return true;
  501. }
  502. @ We determine the cycle $C(e0)$ by following first edges until a back 
  503. edge is encountered. |wk| will be the last node on the tree path and |w0|
  504. is the destination of the back edge. This agrees with the
  505. notation of cite{Me:book}. 
  506. @<determine the cycle ...@>=
  507. node x = source(e0);
  508. node y = target(e0);
  509. edge e = G.first_adj_edge(y);
  510. node wk = y;
  511. while (dfsnum[target(e)] > dfsnum[wk])  // e is a tree edge
  512. { wk = target(e);
  513.   e= G. first_adj_edge(wk);
  514. }
  515. node w0 = target(e);
  516. @ The second phase of |strongly_planar| constructs the connected components of
  517. the interlacing graph of the segments emananating from the the spine of the 
  518. cycle $C(e0)$. We call a connected component a {em block}. For each block
  519. we store the segments comprising its left and right side (lists
  520. |Lseg| and |Rseg| contain the edges
  521. defining these segments) and the ordered list of attachments of the segments
  522. in the block; lists |Latt| and |Ratt| contain the DFS-numbers of the
  523. attachments; high DFS-numbers are at the front of the list. Blocks are
  524. so important that we make them a class.
  525. We need the following operations on blocks.
  526. The constructor takes an edge and a list of attachments and creates 
  527. a block having the edge as the only segment in its left side.
  528. |flip| interchanges the two sides of a block.
  529. |head_of_Latt| and |head_of_Ratt| return the first elements on |Latt| and |Ratt| respectively 
  530. and |Latt_empty| and |Ratt_empty| check these lists for emptyness.
  531. |left_interlace| checks whether the block interlaces with the left side of the topmost block of
  532. stack |S|. |right_interlace| does the same for the right side.
  533. |combine| combines the block with another block |Bprime| by simply concatenating all
  534. lists.
  535. |clean| removes the attachment |w| from the block |B| (it is guaranteed to be the 
  536. first attachment of |B|). If the block becomes empty then it records the 
  537. placement of all segments in the block in the array |alpha| and returns true.
  538. Otherwise it returns false.
  539. |add_to_Att| first makes sure that the right side has no attachment above |w0| 
  540. (by 
  541. flipping); when |add_to_Att| is called at least one side has no
  542. attachment above |w0|.
  543.  |add_to_Att| then adds the lists |Ratt| and |Latt| to the output list |Att|
  544. and records the placement of all segments in the block in |alpha|.
  545. We advise the reader to only skim the rest of the section at this
  546. point and to come back to it when the procedures are first used.
  547. @<typedefs...@>+=
  548. class block
  549. private:
  550. list<int> Latt, Ratt; //list of attachments
  551. list<edge> Lseg,Rseg; //list of segments represented by their defining edges
  552. public:
  553. block(edge e, list<int>& A)
  554. {
  555. Lseg.append(e);
  556. Latt.conc(A);
  557. // the other two lists are empty
  558. }
  559. ~block() @+ {}
  560. void flip()
  561. {
  562. list<int> ha;
  563. list<edge> he;
  564. /* we first interchange |Latt| and |Ratt| and then |Lseg| and |Rseg| */
  565. ha.conc(Ratt); @+ Ratt.conc(Latt); @+ Latt.conc(ha);
  566. he.conc(Rseg); @+ Rseg.conc(Lseg); @+ Lseg.conc(he);
  567. }
  568. int head_of_Latt() @+ { @+ return Latt.head(); @+ }
  569. bool empty_Latt() @+ { @+ return Latt.empty(); @+ }
  570. int head_of_Ratt() @+ { @+ return Ratt.head(); @+ }
  571. bool empty_Ratt() @+ { @+ return Ratt.empty(); @+ }
  572. bool left_interlace(stack<block*>& S)
  573. {
  574. /* check for interlacing with the left side of the topmost block of
  575. |S| */
  576. if (Latt.empty()) error_handler(1,"Latt is never empty");
  577. if (!S.empty() && !((S.top())->empty_Latt()) && 
  578. Latt.tail() < (S.top())->head_of_Latt())
  579. return true;
  580. else return false;
  581. }
  582. bool right_interlace(stack<block*>& S)
  583. {
  584. /* check for interlacing with the right side of the topmost block of
  585. |S| */
  586. if (Latt.empty()) error_handler(1,"Latt is never empty");
  587. if (!S.empty() && !((S.top())->empty_Ratt()) && 
  588. Latt.tail() < (S.top())->head_of_Ratt())
  589. return true;
  590. else return false;
  591. }
  592. void combine(block* & Bprime)
  593. {
  594. /* add block Bprime to the rear of |this| block */
  595. Latt.conc(Bprime -> Latt);
  596. Ratt.conc(Bprime -> Ratt);
  597. Lseg.conc(Bprime -> Lseg);
  598. Rseg.conc(Bprime -> Rseg);
  599. delete(Bprime);
  600. }
  601. bool clean(int dfsnum_w, edge_array<int>& alpha,
  602.  node_array<int>& dfsnum)
  603. {
  604. /* remove all attachments to |w|; there may be several */
  605. while (!Latt.empty() && Latt.head() == dfsnum_w) Latt.pop();
  606. while (!Ratt.empty() && Ratt.head() == dfsnum_w) Ratt.pop();
  607. if (!Latt.empty() || !Ratt.empty()) return false;
  608. /*|Latt| and |Ratt| are empty; we record the placement of the subsegments
  609. in |alpha|. */
  610. edge e;
  611. forall(e,Lseg) alpha[e] = left; 
  612. forall(e,Rseg) alpha[e] = right; 
  613. return true;}
  614. void add_to_Att(list<int>& Att, int dfsnum_w0,
  615.  edge_array<int>& alpha, @| 
  616.  node_array<int>& dfsnum)
  617. /* add the block to the rear of |Att|. Flip if necessary */
  618. if (!Ratt.empty() && head_of_Ratt() > dfsnum_w0) flip();
  619. Att.conc(Latt);
  620. Att.conc(Ratt); 
  621. /* This needs some explanation. Note that |Ratt| is either empty
  622. or ${w0}$. Also if |Ratt| is non-empty then all subsequent sets are contained
  623. in ${w0}$. So we indeed compute an ordered set of attachments. */
  624. edge e;
  625. forall(e,Lseg) alpha[e] = left;
  626. forall(e,Rseg) alpha[e] = right;
  627. }
  628. };
  629. @ We process the edges leaving the spine of $S(e0)$ starting at node |wk|
  630. and working backwards. The interlacing graph of the segments emanating from
  631. the cycle is represented as a stack |S| of blocks. 
  632. @<process all edges leaving the spine@>=
  633. node w = wk;
  634. stack<block*> S;
  635. while (w != x)
  636. { int count = 0;
  637.   forall_adj_edges(e,w)
  638.   {count++;
  639.    if (count != 1) //no action for first edge
  640. {@<test recursively@>;
  641. @<update stack |S| of attachments@>;
  642. } // end if
  643.    } //end forall
  644. @<prepare for next iteration@>;
  645. w = parent[w];
  646. } //end while
  647. @ Let $e$ be any edge leaving the spine. We need to test whether
  648. $S(e)$ is strongly planar and if so compute its list |A| of attachments.
  649. If $e$ is a tree edge we call our procedure recursively and if $e$ is a 
  650. back edge then $S(e)$ is certainly strongly planar and |target(e)| is
  651. the only attachment. If we detect non-planarity we return flase and free
  652. the storage allocated for the blocks of stack |S|.
  653. @<test recursively@>=
  654. list<int> A;
  655. if (dfsnum[w] < dfsnum[target(e)])
  656. { /* tree edge */
  657. if (!strongly_planar(e,G,A,alpha,dfsnum,parent)) 
  658. {while (!S.empty()) delete(S.pop());
  659. return false;
  660. }
  661. }
  662. else A.append(dfsnum[target(e)]);  // a back edge
  663. @ The list |A| contains the ordered list of attachments of segment
  664. $S(e)$. We create an new block consisting only of segment $S(e)$ (in its 
  665. $L$-part) and then combine this block with the topmost block of stack |S| as
  666. long as 
  667. there is interlacing. We check for interlacing with the $L$-part. If there
  668. is interlacing then we flip the two sides of the topmost block. If there
  669. is still interlacing with the left side then the interlacing graph is non-bipartite and 
  670. we declare the graph non-planar (and also free the storage allocated
  671. for the blocks). Otherwise we check for interlacing with
  672. the R-part. If there is interlacing then we combine |B| with the topmost
  673. block and repeat the process with the new topmost block. If there is no
  674. interlacing then we push block |B| onto |S|.
  675. @<update stack ...@>=
  676. block* B = new block(e,A);
  677. while (true)
  678. {
  679. if (B->left_interlace(S)) (S.top())->flip();
  680. if (B->left_interlace(S)) 
  681. {delete(B);
  682. while (!S.empty()) delete(S.pop()); 
  683. return false;
  684. };
  685. if (B->right_interlace(S)) B->combine(S.pop());
  686. else break;
  687. } //end while
  688. S.push(B);
  689. @ We have now processed all edges emanating from vertex |w|. Before starting to 
  690. process edges emanating from vertex |parent[w]| we remove |parent[w]| from 
  691. the list of attachments of the topmost block of stack |S|. If this block
  692. becomes empty then we pop it from the stack and record the placement for all
  693. segments in the block in array |alpha|.
  694. @<prepare for ...@>=
  695. while (!S.empty() && (S.top())->clean(dfsnum[parent[w]],alpha,dfsnum)) delete(S.pop());
  696. @ We test the strong planarity of the segment $S(e0)$. 
  697. We know at this point that the 
  698. interlacing graph is bipartite. Also for each of its connected components the
  699. corresponding block on stack |S| contains the list of attachments below |x|. 
  700. Let |B| be the topmost block of |S|. If both sides of |B| have an attachment 
  701. above |w0| then $S(e0)$ is not strongly planar. We free the storage allocated for
  702. the blocks and return false. Otherwise (cf. procedure
  703. |add_to_Att|) we first make sure that the right side of |B| attaches only to |w0| 
  704. (if at all) and then add the two sides of |B| to the output list |Att|. We also
  705. record the placements of the subsegments in |alpha|.
  706. @<test strong planarity and ...@>=
  707. Att.clear();
  708. while(! S.empty())
  709. {
  710. block* B = S.pop();
  711. if (!(B->empty_Latt()) && !(B->empty_Ratt()) &&
  712. (B->head_of_Latt()>dfsnum[w0]) && (B->head_of_Ratt() > dfsnum[w0]))
  713. {delete(B);while (!S.empty()) delete(S.pop()); return false;}
  714. B->add_to_Att(Att,dfsnum[w0],alpha,dfsnum);
  715. delete(B);
  716. } //end while
  717. /* Let's not forget (as the book does) that $w0$ is an attachment of $S(e0)$
  718. except if $w0 = x$. */
  719. if (w0 != x) Att.append(dfsnum[w0]);
  720. @* Constructing the Embedding.    label{embedding}
  721. %%%%%%%%%%%%%%%%%% einbinden von bildern mit Unterschrift %%%%%%%%%%%%%
  722. newcommand{bild}[2]{
  723. begin{figure}[htb]
  724. begin{center}
  725. input{#1.tex}
  726. end{center}
  727. caption{{#2}label{#1}}   
  728. end{figure}
  729. %%%%%  wird so benutzt: bild{<label>}{<caption>}  %%%%%%%%%%%%%%%%%%%%
  730. %%%%%  z.B. bild{part}{A part of the augmented segment tree structure}
  731. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  732. newtheorem{theorem}{Theorem}
  733. We now discuss how the planarity testing algorithm can be extended
  734. so that it also computes a planar map.
  735. Consider a segment $S(e_0)=C+S(e_1)+ldots +S(e_m)$ consisting of
  736. cycle $C$ and emanating segments $S(e_1),ldots ,S(e_m)$ and
  737. recall that the proofs of Lemmas 8 and 9 describe how
  738. the embeddings of the $S(e_i)$'s have to be combined to yield a
  739. canonical embedding of $S(e_0)$.
  740. Our goal is to turn these proofs into an efficient algorithm.
  741. The proofs of Lemmas 8 and 9 demonstrate two things:
  742. begin{itemize}
  743. item How to test whether $IG(C)$ is bipartite and how to construct
  744.   a partition ${L,R}$ of its vertex set, and
  745. item how to construct an embedding of $S(e_0)$ from the embeddings of
  746.   the $S(e_i)$'s. This involves flipping of embeddings as we
  747.   incrementally construct the embedding of $S(e_0)$.
  748. end{itemize}
  749. Suppose now that some benign agent told us that $IG(C)$ were bipartite
  750. and gave us an appropriate partition ${L,R}$ of its vertex set,
  751. i.e., a partition ${L,R}$ such that no
  752. two segments in $L$ and no two segments in $R$ interlace and such that
  753. $A(e_i)cap {w_1,ldots ,w_{r-1}}=emptyset$ for any segment
  754. $S(e_i)in R$.
  755. Here, as before, $w_0,ldots ,w_r$ denotes the stem of $C$.
  756. Then no flipping would ever be necessary;
  757. we can simply combine the embeddings of the $S(e_i)$'s as prescribed
  758. by the partition ${L,R}$.
  759. More precisely, to construct a canonical embedding of $S(e_0)$ draw
  760. the path $w_0,ldots ,w_k$ (consisting of stem $w_0,ldots ,w_r$,
  761. edge $e_0 = (w_r,w_{r+1})$ and spine $w_{r+1},ldots ,w_k$) as a
  762. vertical upwards directed path, add edge $(w_k,w_0)$, and then for $i$,
  763. $1leq ileq m$, and $S(e_i)in L$ extend the embedding of
  764. $C+S(e_1)+ldots S(e_{i-1})$ by glueing a canonical embedding of
  765. $S(e_i)$ onto the left side of the vertical path, and for $i$,
  766. $1leq ileq m$, and $S(e_i)in R$ extend the embedding of
  767. $C+S(e_1)+ldots +S(e_{i-1})$ by glueing a reversed canonical 
  768. embedding of $S(e_i)$ onto the right side of the vertical path.
  769. Similarly, if the goal is to construct a reversed canonical embedding
  770. of $S(e_0)$ then, if $S(e_i)in L$, a reversed canonical embedding of
  771. $S(e_i)$ is glued onto the right side of the vertical path, and if
  772. $S(e_i) in R$, then a canonical embedding of $S(e_i)$ is glued onto the
  773. left side of the vertical path.
  774. Who is the benign agent which tells us that $IG(C)$ is bipartite and
  775. gives us the appropriate partition ${L,R}$ of the segments emanating
  776. from $C=C(e_0)$?
  777. It's the call {em stronglyplanar}($e_0$).
  778. After all, it tests whether $IG(C)$ is bipartite and computes a
  779. bipartition of its vertex set.
  780. Let $S(e)$ be a segment emanating from $C$ and let $B$ be the connected
  781. component of $IG(C)$ containing $S(e)$.
  782. The call {em stronglyplanar}($e_0$) computes $B$ iteratively.
  783. The construction of $B$ is certainly completed when $B$ is popped from
  784. stack $S$.
  785. Put $S(e)$ into $R$ when $S(e)in RB$ at that moment and put $S(e)$
  786. into $L$ otherwise.
  787. With this extension, algorithm {em stronglyplanar} computes the
  788. partition ${L,R}$ of the segments emanating from $C$ in linear time.
  789. We assume for notational convenience that the partition (more precisely,
  790. the union of all partitions for all cycles $C(e_0)$ encountered in the
  791. algorithm) is given as a function $alpha :Sto {L,R}$ where $S$ is
  792. the set of edges for which {em stronglyplanar} is called.
  793. We next give the algorithmic details of the embedding process.
  794. We first use procedure {em stronglyplanar} to compute the mapping
  795. $alpha$.
  796. We then use a procedure {em embedding} to actually compute an
  797. embedding.
  798. The procedure {em embedding} takes two parameters: an edge $e_0$ and
  799. a flag $tin {L,R}$.
  800. A call {em embedding}($e_0,L$) computes a canonical embedding of 
  801. $S(e_0)$ and a call {em embedding}($e_0,R$) computes a reversed
  802. canonical embedding of $S(e_0)$.
  803. The call {em embedding}($(1,2),L$) embeds the entire graph.
  804. The embedding of $S(e_0)$ computed by {em embedding}$(e_0,t)$ is
  805. represented in the following non-standard way:
  806. begin{enumerate}
  807. item For the vertices $vin V(e_0)$ we use the standard
  808.   representation, i.e., the cyclic list of the incident
  809.   edges corresponding to the clockwise ordering of the edges
  810.   in the embedding.
  811. item For the vertices in the stem we use a non-standard representation.
  812.   For each vertex $w_iin{w_0,ldots,w_{r}}$ let the lists
  813.   $AL(w_i)$ and $AR(w_i)$ be such that the catenation of
  814.   $(w_i,w_{i+1})$, $AR(w_i)$, $(w_i,w_{i-1})$, and
  815.   $AL(w_i)$ corresponds to the clockwise ordering of the edges
  816.   incident to $w_i$ in the embedding. Here, $w_{-1}=w_k$.
  817.   Note that $AR(w_i)=emptyset$ for $1leq i<r$ if $t=L$, and
  818.   $AL(w_i)=emptyset$ for $1leq i<r$, if $t=R$.
  819.   The lists $AL(w_i)$, $AR(w_i)$, $0leq ileq r$, are returned in an
  820.   implicit way: $AL(w_r)$ and $AR(w_r)$ are returned as the list
  821.   $T=AL(w_r),(w_r,w_{r+1})$, $AR(w_r)$ and the other lists
  822.   are returned as the list $A=$ $AR(w_{r-1}),ldots,$
  823.   $AR(w_0),(w_0,w_k),AL(w_0),ldots ,AL(w_{r-1})$,
  824.   cf. Figure~ref{result-embedding.pstex_t}.
  825. end{enumerate}
  826. bild{result-embedding.pstex_t}{A call {em embedding} $(e_0,t)$ returns lists $T$ and $A$.}
  827. The procedure {em embedding} has the same structure as the procedure
  828. {em stronglyplanar} and is given in Program 1 on page pageref{program}. 
  829. It first constructs the stem and the spine (line (1)) of cycle $C(e_0)$,
  830. then walks down the spine (lines (3) to (14)), and finally computes
  831. the lists $T$ and $A$ it wants to return (lines (15) and (16)).
  832. We first discuss the walk down the spine.
  833. Suppose that the walk has reached vertex $w_j$.
  834. We first recursively process the edges emanating from $w_j$
  835. (lines (4) to (10)), and then compute the cyclic adjacency list of vertex
  836. $w_j$ and prepare for the next iteration (lines (11) to (13)).
  837. We discuss lines (4) to (10) first.
  838. In general, some number of edges emanating from $w_j$ and all edges
  839. incident to vertices $w_l$ with $l>j$ will have been processed already.
  840. In agreement with our previous notation call the processed edges
  841. $e_1,ldots ,e_{i-1}$.
  842. We claim that the following statement is an invariant of the loop (4) to (10):
  843. $T$ concatenated with $(w_j,w_{j-1})$ is the cyclic adjacency list of
  844. vertex $w_j$ in the embedding of $C+S(e_1)+ldots +S(e_{i-1})$, and
  845. $AL$ and $AR$ are the catenation of lists $AL(w_0),ldots ,AL(w_{j-1})$
  846. and $AR(w_{j-1}),ldots ,AR(w_0)$ respectively where $(w_l,w_{l+1})$,
  847. $AR(w_l), (w_l,w_{l-1}), AL(w_l)$ is the cyclic adjacency
  848. list of vertex $w_l$, $0leq lleq j-1$, in the embedding of 
  849. $C+S(e_0)+ldots +S(e_{i-1})$.
  850. The lists $T$, $AL$, and $AR$ are certainly initialized correctly in
  851. line (2).
  852. Assume now that we process edge $e'=e_i$ emanating from $w_j$.
  853. The flag $alpha(e')$ indicates what kind of embedding of $S(e_i)$ is
  854. needed to build a canonical embedding of $S(e_0)$; the opposite kind of
  855. embedding of $S(e_i)$ is needed to build a reversed canonical embedding
  856. of $S(e_0)$.
  857. So the required kind is given by $toplusalpha(e')$, where 
  858. $Loplus L=Roplus R=L$ and $Loplus R=Roplus L=R$.
  859. The call {em embedding}$(e',toplusalpha(e'))$ computes the cyclic
  860. adjacency lists of the vertices in $V(e')$ and returns lists $T'$ and
  861. $A'$ as defined above.
  862. If $S(e_i)$ has to be glued to the left side of the vertical path
  863. $w_0,ldots ,w_k$, i.e., if  $t=alpha(e')$ then we append $T'$ to the front of $T$ and $A'$ to
  864. the end of $AL$, cf. Figure~ref{glueing}.
  865. Analogously, if $S(e_i)$ has to be glued to the right side of the
  866. path $w_0,ldots ,w_k$, i.e., if $tnot=alpha(e')$, then we append $T'$
  867. to the end of $T$ and $A'$ to the front of $AR$.
  868. This clearly maintains the invariant.
  869. Suppose now that we have processed all edges emanating from $w_j$.
  870. Then $(w_j,w_{j-1})$ concatenated with $T$ is the cyclic adjacency list
  871. of vertex $w_j$ (line (11)).
  872. begin{figure}[htb]
  873. begin{center}
  874. input{glueing.pstex_t}
  875. end{center}
  876. caption{label{glueing}
  877. Glueing $S(e')$ to the left or right side of the path
  878. $w_0,ldots ,w_k$ respectively.}
  879. end{figure}
  880. We next prepare for the treatment of vertex $w_{j-1}$.
  881. Let $T'$ and $T''$ be the list of darts incident to $w_{j-1}$ from
  882. the left and from the right respectively and having their other
  883. endpoint in an already embedded segment.
  884. List $T'$ is a suffix of $AL$ and list $T''$ is a prefix of $AR$.
  885. The catenation of $T',(w_{j-1},w_j)$, $T''$, and
  886. $(w_{j-1},w_{j-2})$ is the current clockwise adjacency list of
  887. vertex $w_{j-1}$.
  888. Thus lines (12) and (13) correctly initialize $AL$, $AR$, and $T$ for
  889. the next iteration.
  890. Suppose now that all edges emanating from the spine of $C(e_0)$ have
  891. been processed, i.e., control reaches line (15).
  892. At this point, list $T$ is the ordered list of darts incident to $w_r$
  893. (except $(w_r,w_{r-1})$) and the two lists $AL$ and $AR$ are the
  894. ordered list of darts incident to the two sides of the stem of $C(e_0)$.
  895. Thus $T$ and the catenation of $AR,(w_0,w_k)$, and
  896. $AL$ are the two components of the output of {em embedding}$(e_0,t)$.
  897. We summarize in
  898. begin{theorem}
  899. Let $G=(V,E)$ be a planar graph.
  900. Then $G$ can be turned into a planar map $(G,sigma)$ in linear time.
  901. end{theorem}
  902. begin{table}
  903. ~hrulefill
  904. begin{tabbing}
  905. qquad = {bf do} = {bf do} = kill
  906. > (0) ' {bf procedure} {em embedding}($e_0$: edge, $t$: ${L,R}$)+\
  907.          ($*$ computes an embedding of $S(e_0)$, $e_0=(x,y)$, as described in the text; \
  908.          > it returns the lists $T$ and $A$ defined in the text $*$)-\
  909. > (1) ' find the spine of segment $S(e_0)$ by starting in node $y$ and always+\
  910.        > take the first edge of every adjacency list until a back edge is \
  911.        > encountered. This back edge leads to node $w_0=lowpt[y]$. \
  912.        > Let $w_0,ldots,w_r$ be the tree path from $w_0$ to $x=w_r$ and \
  913.        > let $w_{r+1}=y,ldots,w_k$ be the spine constructed above. -\
  914. > (2) ' $AL gets AR gets$ empty list of darts;\
  915. >     > $T gets (w_k,w_0)$; ` ($*$ a list of darts $*$)\
  916. > (3) ' {bf for} $j$ {bf from} $k$ {bf downto} $r+1$ \
  917. > (4) ' {bf do} {bf for} all edges $e'$ (except the first) emanating from $w_j$ \
  918. > (5) ' > {bf do} $(T',A') gets$ {em embedding}$(e',toplusalpha(e'))$\
  919. > (6) ' > > {bf if} $t=alpha(e')$\
  920. > (7) ' > > {bf then} $T gets T'$ {bf conc} $T$; $AL gets AL$ {bf conc} $A'$\
  921. > (8) ' > > {bf else} $T gets T$ {bf conc} $T'$; $AR gets A'$ {bf conc}  $AR$\
  922. > (9) ' > > {bf fi}\
  923. >(10) ' > {bf od}\
  924. >(11) ' > {bf output} $(w_j,w_{j-1})$ {bf conc} $T$; 
  925. ` ($*$ the cyclic adjacency list of vertex $w_j$ $*$) \
  926. >(12) ' > {bf let} $AL=AL'$ {bf conc} $T'$ and $AR=T''$ 
  927. {bf conc} $AR'$\
  928. >     > > where $T'$ and $T''$ contain all darts incident 
  929. to $w_{j-1}$;\
  930. >(13) ' > $ALgets AL'$; $ARgets AR'$; $Tgets 
  931. T'$ {bf conc} $(w_{j-1},w_j)$ {bf conc} $T''$\
  932. >(14) ' {bf od}\
  933. >(15) ' $Agets$ $AR$ {bf conc} $(w_0,w_k)$ {bf conc} $AL$;\
  934. >(16) ' {bf return} $T$ and $A$\
  935. >(17) ' {bf end}
  936. end{tabbing}
  937. ~hrulefill Program 1 label{program} hrulefill 
  938. end{table}
  939. In our implementation we follow the book except in three minor points. |G| has only one directed
  940. version of each edge but |H| has both. In the embedding 
  941. phase we need both 
  942. directions and therefore construct the embedding of 
  943. |H| and later translate it back to |Gin|. Secondly, we do not construct the embedding
  944. of |H| vertex by vertex but in one shot. To that effect we compute a labelling
  945. |sort_num| of the edges of |H| and later sort the edges.
  946. Thirdly, the book makes reference to edges $(w_{i-1},w_i)$
  947. and their reversals. To make these edges available we compute an array |tree_edge_into|
  948. that contains for each node the incoming tree edge. 
  949. We finally want to remark on our convention for drawing lists. 
  950. In Figures ref{result-embedding.pstex_t}
  951. and ref{glueing} the arrows indicate the end (!!!) of the lists.
  952. clearpage
  953. @<construct embedding@>=
  954. {list<edge> T,A;  //lists of edges of |H|
  955. int cur_nr = 0;
  956. edge_array<int> sort_num(H);
  957. node_array<edge> tree_edge_into(G);
  958. embedding(G.first_adj_edge(G.first_node()),left,G,alpha,dfsnum,T,A,
  959. cur_nr,sort_num,tree_edge_into,parent,reversal);
  960. /* |T| contains all edges incident to the first node except the cycle edge into it. 
  961. That edge comprises |A| */
  962. T.conc(A);
  963. edge e;
  964. forall(e,T) sort_num[e] = cur_nr ++;
  965. edge_array<int> sort_Gin(Gin);
  966. {edge ein;
  967. forall_edges(ein,Gin) sort_Gin[ein] = sort_num[companion_in_H[ein]];
  968. }
  969. Gin.sort_edges(sort_Gin);
  970. }
  971. @ It remains to describe procedure |embedding|.
  972. @<auxiliary ...@>+=
  973. void embedding(edge e0, int t, GRAPH<node,edge>& G,
  974.  edge_array<int>& alpha, @| 
  975.  node_array<int>& dfsnum, 
  976.  list<edge>& T, list<edge>& A, int& cur_nr, @| 
  977.  edge_array<int>& sort_num, node_array<edge> & tree_edge_into, @|
  978.  node_array<node>& parent, edge_array<edge>& reversal)
  979. {@<embed: determine the cycle $C(e0)$@>; 
  980. @<process the subsegments@>;
  981. @<prepare the output@>;
  982. }
  983. @ We start by determining the spine cycle. This is precisley as in |strongly_planar|. 
  984. We also record for the vertices $w_{r+1}$, $ldots$, $w_k$, and $w_0$ the 
  985. incoming cycle edge either in |tree_edge_into| or in the local
  986. variable |back_edge_into_w0|. This is line (1) of Program1.
  987. @<embed: determine ...@>=
  988. node x = source(e0);
  989. node y = target(e0);
  990. tree_edge_into[y] = e0;
  991. edge e = G.first_adj_edge(y);
  992. node wk = y;
  993. while (dfsnum[target(e)] > dfsnum[wk])  // e is a tree edge
  994. { wk = target(e);
  995.   tree_edge_into[wk] = e;
  996.   e= G. first_adj_edge(wk);
  997. }
  998. node w0 = target(e);
  999. edge back_edge_into_w0 = e;
  1000. @ Lines (2) to (14).
  1001. @<process the subsegments@>=
  1002. node w = wk;
  1003. list<edge> Al, Ar;
  1004. list<edge> Tprime, Aprime;
  1005. T.clear(); 
  1006. T.append(G[e]);    // |e = (wk,w0)| at this point, line (2)
  1007. while (w != x)
  1008. { int count = 0;
  1009.   forall_adj_edges(e,w)
  1010.   {count++;
  1011.    if (count != 1) //no action for first edge
  1012. {@<embed recursively@>;
  1013. @<update lists |T|, |Al|, and |Ar|@>;
  1014. } // end if
  1015.    } //end forall
  1016. @<compute |w|'s adjacency list and prepare for next iteration@>;
  1017. w = parent[w];
  1018. } //end while
  1019. @ Line (5). The book does not distinguish between tree and back edges but we do
  1020. here.
  1021. @<embed recursively@>=
  1022. if (dfsnum[w] < dfsnum[target(e)])
  1023. { /* tree edge */
  1024. int tprime = ((t == alpha[e]) ? left : right);
  1025. embedding(e,tprime,G,alpha,dfsnum,Tprime,Aprime,cur_nr,sort_num,tree_edge_into,parent,reversal);
  1026. }
  1027. else  {/* back edge */
  1028. Tprime.append(G[e]); //$e$
  1029. Aprime.append(reversal[G[e]]);      //reversal of $e$
  1030. }
  1031. @ Lines (6) to (9).
  1032. @<update lists |T|, ...@>=
  1033. if (t == alpha[e])
  1034. {Tprime.conc(T);
  1035. T.conc(Tprime);    //$T = Tprime conc T$
  1036. Al.conc(Aprime);  //$Al = Al conc Aprime$
  1037. }
  1038. else {T.conc(Tprime);  // $ T = T conc Tprime $
  1039. Aprime.conc(Ar);
  1040. Ar.conc(Aprime);  // $ Ar = Aprime conc Ar$
  1041. }
  1042. @ Lines (11) to (13).
  1043. @<compute |w|'s ...@>=
  1044. T.append(reversal[G[tree_edge_into[w]]]);    // $(w_{j-1},w_j)$
  1045. forall(e,T) sort_num[e] = cur_nr ++;
  1046. /* |w|'s adjacency list is now computed; we clear |T| and prepare for the 
  1047. next iteration by moving all darts incident to |parent[w]| from |Al| and
  1048. |Ar| to |T|. These darts are at the rear end of |Al| and at the front end
  1049. of |Ar| */
  1050. T.clear();
  1051. while (!Al.empty() && source(Al.tail()) == G[parent[w]])   
  1052. // |parent[w]| is in |G|, |Al.tail| in |H|
  1053. {T.push(Al.Pop());   //Pop removes from the rear
  1054. }
  1055. T.append(G[tree_edge_into[w]]);    // push would be equivalent
  1056. while (!Ar.empty() && source(Ar.head()) == G[parent[w]])   // 
  1057. {T.append(Ar.pop());  // pop removes from the front
  1058. }
  1059. @ Line (15). Concatenate |Ar|, $(w_0,w_r)$, and |Al|.
  1060. @<prepare the output@>=
  1061. A.clear();
  1062. A.conc(Ar);
  1063. A.append(reversal[G[back_edge_into_w0]]);
  1064. A.conc(Al);
  1065. @* Efficiency. label{Efficiency}
  1066. Under LEDA 3.0 the space requirement of the first version of |planar| is approximately
  1067. $160 (n+m) +100 alpha m$ Bytes, where $n$ and $m$ denote the number of nodes 
  1068. and edges respectively and $alpha$ is the fraction of edges in the input graph
  1069. that do not have their reversal in the input graph. For the pseudo-random
  1070. planar graphs generated in the demo we have $alpha = 0$ and $m = 4n$ and hence the
  1071. space requirement is about $800 n$ Bytes. The second version needs an additional
  1072. $54n + 66m$ Bytes.
  1073. The running time of |planar| is about $50$ times the running 
  1074. time of |STRONG_COMPONENTS|. On a 50 MIPS SPARC10 workstation 
  1075. the planarity of a
  1076. planar graph with 16000 nodes and 30000 edges ($alpha = 0$) is tested in 
  1077. about 10 seconds. It takes 5.4 seconds to make the graph bidirected and 
  1078. biconnected, about 3.9 seconds to test its planarity, and 
  1079. another 6.1 seconds to
  1080. construct an embedding. The space requirement is about 15 MByte.
  1081. @* A Demo. label{demo}
  1082. The demo allows the user to either interactively construct a graph 
  1083. using 
  1084. LEDA's graph editor or to construct a random graph, or to 
  1085. construct a ``pseudo-random'' planar graph 
  1086. (the graph defined by an arrangement of random line segments). The graph is 
  1087. then tested for
  1088. planarity. If the graph is planar a straight-line embedding is output. If the
  1089. graph is non-planar a Kuratowski subgraph is highlighted.
  1090. The demo proceeds in cycles. In each cycle we first clear the graphics window |W| and
  1091. the graph |G| and then give the user the choice of a new input graph.
  1092. @(demo.c@>=
  1093. @<includes@>;
  1094. @<procedure to draw graphs@>;
  1095. main(){
  1096. @<initiation and declarations@>;
  1097. while (true){
  1098. @<select graph@>;
  1099. @<test graph for planarity and show output@>
  1100. @<reset window@>;
  1101. }
  1102. return 0;
  1103. }
  1104. @ We need to include |planar.h| and various parts of LEDA.
  1105. @<includes@>=
  1106. #include "planar.h"
  1107. #include <LEDA/graph.h>
  1108. #include <LEDA/graph_alg.h>
  1109. #include <LEDA/window.h>
  1110. #include <LEDA/graph_edit.h>
  1111. @ We need a simple procedure to draw a graph in a graphics window. The
  1112. numbering of the nodes is optional.
  1113. @<procedure to draw graphs@>=
  1114. void draw_graph(const GRAPH<point,int>& G, window& W, bool numbering=false)
  1115. { node v;
  1116.   edge e;
  1117.   int i = 0;
  1118.   forall_edges(e,G)
  1119.     W.draw_edge(G[source(e)],G[target(e)],blue);
  1120.   if (numbering)
  1121.      forall_nodes(v,G) W.draw_int_node(G[v],i++,red);
  1122.   else
  1123.      forall_nodes(v,G) W.draw_filled_node(G[v],red);
  1124. }
  1125. @ We give the user a short explanation of the demo and declare some variables.
  1126. @<initiation and declarations@>=
  1127. panel P;
  1128. P.text_item("This demo illustrates planarity testing and planar straight-line");
  1129. P.text_item("embedding. You have two ways to construct a graph: either interactively");
  1130. P.text_item("using the LEDA graph editor or by calling one of two graph generators.");
  1131. P.text_item("The first generator constructs a random graph with a certain");
  1132. P.text_item("number of nodes and edges (you will be asked how many) and the ");
  1133. P.text_item("second generator constructs a planar graph  by intersecting a certain");
  1134. P.text_item("number of random line segments in the unit square (you will be asked how many)."); 
  1135. P.text_item(" ");
  1136. P.text_item("The graph is displayed and then tested for planarity.");
  1137. P.text_item("If the graph is non-planar a Kuratowski subgraph is highlighted.");
  1138. P.text_item("If the graph is planar, a straight-line drawing is produced.");
  1139. P.button("continue");
  1140. P.open();
  1141. window W;
  1142. GRAPH<point,int> G;
  1143. node v,w;
  1144. edge e;
  1145. int n = 250;
  1146. int m = 250;
  1147. const double pi= 3.14;
  1148. panel P1("PLANARITY TEST");
  1149. P1.int_item("|V| = ",n,0,500);
  1150. P1.int_item("|E| = ",m,0,500);
  1151. P1.button("edit");
  1152. P1.button("random");
  1153. P1.button("planar");
  1154. P1.button("quit");
  1155. P1.text_item(" ");
  1156. P1.text_item("The first slider asks for the number n of nodes and");
  1157. P1.text_item("the second slider asks for the number m of edges.");
  1158. P1.text_item("If you select the random input button then a graph with");
  1159. P1.text_item("that number of nodes and edges is constructed, if you");
  1160. P1.text_item("select the planar input button then 2.5 times square-root of n");
  1161. P1.text_item("random line segments are chosen and intersected to yield");
  1162. P1.text_item("a planar graph with about n nodes, and if you select the");
  1163. P1.text_item("edit button then the graph editor is called.");
  1164. P1.text_item(" ");
  1165. @ We display the panel |P1| until the user makes his choice. Then we construct
  1166. the appropriate graph.
  1167. @<select graph@>= 
  1168.   int inp = P1.open(W);   // P1 is displayed until a button is pressed
  1169.   if (inp == 3) break;   // quit button pressed
  1170.   W.init(0,1000,0);
  1171.   W.set_node_width(5);
  1172.   switch(inp){
  1173.   case 0: { // graph editor
  1174.     W.set_node_width(10);
  1175.             G.clear();
  1176.             graph_edit(W,G,false);
  1177.             break;
  1178.            }
  1179.   
  1180.   case 1: { // random graph
  1181.             G.clear();
  1182.             random_graph(G,n,m);
  1183.             /* eliminate parallel edges and self-loops */
  1184.             eliminate_parallel_edges(G);
  1185.             
  1186.             list<edge>Del= G.all_edges();
  1187.             forall(e,Del) 
  1188.                if (G.source(e)==G.target(e)) G.del_edge(e);
  1189.             /* draw the graph with its nodes on a circle*/
  1190.             float ang = 0;
  1191.   
  1192.             forall_nodes(v,G)
  1193.             { G[v] = point(500+400*sin(ang),500+400*cos(ang));
  1194.               ang += 2*pi/n;
  1195.              }
  1196.   
  1197.              draw_graph(G,W);
  1198.   
  1199.              break;
  1200.            }
  1201.   
  1202.   case 2: { // pseudo-random planar graph
  1203.     node_array<double> xcoord(G);
  1204.             node_array<double> ycoord(G);
  1205.             G.clear();
  1206.             random_planar_graph(G,xcoord,ycoord,n);
  1207.             forall_nodes(v,G)
  1208.                G[v] = point(1000*xcoord[v], 900*ycoord[v]);
  1209.   
  1210.              draw_graph(G,W);
  1211.   
  1212.             break;
  1213.            }
  1214.   
  1215.     }
  1216.   
  1217.   
  1218. @ We test the planarity of our graph |G| using our procedure |planar|.
  1219. @<test graph for planarity and ...@>=
  1220.   if (PLANAR(G,false))
  1221.   { 
  1222.     if(G.number_of_nodes()<4)
  1223.         W.message("That's an insult: Every graph with |V| <= 4 is planar");
  1224.     else
  1225.       { W.message("G is planar. I compute a straight-line embedding ...");
  1226.   
  1227.         /* we first make |G| bidirected. We remember the edges added in |n_edges| */
  1228.         node_array<int>nr(G);
  1229.         edge_array<int>cost(G);
  1230.         int cur_nr= 0;
  1231.         int n = G.number_of_nodes();
  1232.         node v;
  1233.         edge e;
  1234.         
  1235.         forall_nodes(v,G)nr[v]= cur_nr++;
  1236.         
  1237.         forall_edges(e,G)cost[e]= ((nr[source(e)]<nr[target(e)])?
  1238.         n*nr[source(e)]+nr[target(e)]:
  1239.         n*nr[target(e)]+nr[source(e)]);
  1240.         
  1241.         G.sort_edges(cost);
  1242.         
  1243.         list<edge> L= G.all_edges();
  1244.   
  1245.         list<edge> n_edges;
  1246.   
  1247.         while(!L.empty())
  1248.         { e= L.pop();
  1249.   
  1250.           if( ! L.empty() && source(e)==target(L.head())   
  1251.                           && target(e)==source(L.head()))
  1252.              L.pop();
  1253.           else
  1254.             { n_edges.append(G.new_edge(target(e),source(e)));
  1255.              }
  1256.          }
  1257.   
  1258.           Make_biconnected_graph(G);
  1259.   
  1260.           PLANAR(G,true);
  1261.   
  1262.           node_array<int> xcoord(G),ycoord(G);
  1263.   
  1264.           STRAIGHT_LINE_EMBEDDING(G,xcoord,ycoord);
  1265.   
  1266.           float f = 900.0/(2*G.number_of_nodes());
  1267.   
  1268.           forall_nodes(v,G) G[v] = point(f*xcoord[v]+30,2*f*ycoord[v]+30);
  1269.   
  1270.           forall(e,n_edges) G.del_edge(e);
  1271.   
  1272.           W.clear();
  1273.           if (inp == 0) 
  1274.              draw_graph(G,W,true); // with node numbering
  1275.           else
  1276.              draw_graph(G,W);
  1277.   
  1278.         }
  1279.      }
  1280.   else
  1281.     { W.message("Graph is not planar. I compute the Kuratowski subgraph ...");
  1282.       list<edge>L;
  1283.       PLANAR(G,L,false);
  1284.       node_array<int> deg(G,0);
  1285.       int lw = W.set_line_width(3);
  1286.       edge e;
  1287.       forall(e,L) 
  1288.       { node v = source(e);
  1289.         node w = target(e);
  1290.         deg[v]++;
  1291.         deg[w]++;
  1292.         W.draw_edge(G[v],G[w]);
  1293.        }
  1294.       int i = 1;
  1295.       /* We highlight the Kuratowski subgraph. Nodes with degree are drawn black.
  1296. The nodes with larger degree are shown green and numbered from 1 to 6 */
  1297.       forall_nodes(v,G) 
  1298.       { 
  1299.         if (deg[v]==2) W.draw_filled_node(G[v],black);
  1300.         if (deg[v] > 2)
  1301.         { int nw = W.set_node_width(10);
  1302.           W.draw_int_node(G[v],i++,green);
  1303.           W.set_node_width(nw);
  1304.          }
  1305.       }
  1306.       W.set_line_width(lw);
  1307.    }
  1308. @ We reset the graphics window.
  1309. @<reset ...@>=
  1310.    W.set_show_coordinates(false);
  1311.    W.set_frame_label("click any button to continue");
  1312.    W.read_mouse(); // wait for a click
  1313.    W.reset_frame_label();
  1314.    W.set_show_coordinates(true);
  1315. @* Some Theory.    label{reprint}
  1316. We give the theory underlying the planarity test as described in
  1317. cite[section IV.10]{Me:book}.
  1318. %Our next ...
  1319. bibliography{/KM/usr/mehlhorn/tex/my}
  1320. bibliographystyle{alpha}
  1321. end{document}