Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphK4Search_Extensions.c
4077 views
1
/*
2
Planarity-Related Graph Algorithms Project
3
Copyright (c) 1997-2010, John M. Boyer
4
All rights reserved. Includes a reference implementation of the following:
5
6
* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,
7
Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,
8
University of Victoria, 2001.
9
10
* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)
11
Planarity by Edge Addition". Journal of Graph Algorithms and Applications,
12
Vol. 8, No. 3, pp. 241-273, 2004.
13
14
* John M. Boyer. "A New Method for Efficiently Generating Planar Graph
15
Visibility Representations". In P. Eades and P. Healy, editors,
16
Proceedings of the 13th International Conference on Graph Drawing 2005,
17
Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.
18
19
Redistribution and use in source and binary forms, with or without modification,
20
are permitted provided that the following conditions are met:
21
22
* Redistributions of source code must retain the above copyright notice, this
23
list of conditions and the following disclaimer.
24
25
* Redistributions in binary form must reproduce the above copyright notice, this
26
list of conditions and the following disclaimer in the documentation and/or
27
other materials provided with the distribution.
28
29
* Neither the name of the Planarity-Related Graph Algorithms Project nor the names
30
of its contributors may be used to endorse or promote products derived from this
31
software without specific prior written permission.
32
33
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
37
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
40
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
*/
44
45
#include <stdlib.h>
46
47
#include "graphK4Search.private.h"
48
#include "graphK4Search.h"
49
50
extern int _GetNextVertexOnExternalFace(graphP theGraph, int curVertex, int *pPrevLink);
51
52
extern int _SearchForK4InBicomps(graphP theGraph, int I);
53
extern int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int I, int R);
54
55
extern int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts,
56
int *degrees, int *imageVerts);
57
58
extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree,
59
int *imageVerts, int maxNumImageVerts);
60
61
extern int _TestSubgraph(graphP theSubgraph, graphP theGraph);
62
63
/* Forward declarations of local functions */
64
65
void _K4Search_ClearStructures(K4SearchContext *context);
66
int _K4Search_CreateStructures(K4SearchContext *context);
67
int _K4Search_InitStructures(K4SearchContext *context);
68
69
/* Forward declarations of overloading functions */
70
71
int _K4Search_CreateFwdArcLists(graphP theGraph);
72
void _K4Search_CreateDFSTreeEmbedding(graphP theGraph);
73
void _K4Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink);
74
int _K4Search_MarkDFSPath(graphP theGraph, int ancestor, int descendant);
75
int _K4Search_HandleBlockedEmbedIteration(graphP theGraph, int I);
76
int _K4Search_HandleBlockedDescendantBicomp(graphP theGraph, int I, int RootVertex, int R, int *pRout, int *pW, int *pWPrevLink);
77
int _K4Search_EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult);
78
int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);
79
int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);
80
81
void _K4Search_InitGraphNode(graphP theGraph, int I);
82
void _InitK4SearchGraphNode(K4SearchContext *context, int I);
83
void _K4Search_InitVertexRec(graphP theGraph, int I);
84
void _InitK4SearchVertexRec(K4SearchContext *context, int I);
85
86
int _K4Search_InitGraph(graphP theGraph, int N);
87
void _K4Search_ReinitializeGraph(graphP theGraph);
88
int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);
89
90
/* Forward declarations of functions used by the extension system */
91
92
void *_K4Search_DupContext(void *pContext, void *theGraph);
93
void _K4Search_FreeContext(void *);
94
95
/****************************************************************************
96
* K4SEARCH_ID - the variable used to hold the integer identifier for this
97
* extension, enabling this feature's extension context to be distinguished
98
* from other features' extension contexts that may be attached to a graph.
99
****************************************************************************/
100
101
int K4SEARCH_ID = 0;
102
103
/****************************************************************************
104
gp_AttachK4Search()
105
106
This function adjusts the graph data structure to attach the K4 search
107
feature.
108
****************************************************************************/
109
110
int gp_AttachK4Search(graphP theGraph)
111
{
112
K4SearchContext *context = NULL;
113
114
// If the K4 search feature has already been attached to the graph,
115
// then there is no need to attach it again
116
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
117
if (context != NULL)
118
{
119
return OK;
120
}
121
122
// Allocate a new extension context
123
context = (K4SearchContext *) malloc(sizeof(K4SearchContext));
124
if (context == NULL)
125
{
126
return NOTOK;
127
}
128
129
// First, tell the context that it is not initialized
130
context->initialized = 0;
131
132
// Save a pointer to theGraph in the context
133
context->theGraph = theGraph;
134
135
// Put the overload functions into the context function table.
136
// gp_AddExtension will overload the graph's functions with these, and
137
// return the base function pointers in the context function table
138
memset(&context->functions, 0, sizeof(graphFunctionTable));
139
140
context->functions.fpCreateFwdArcLists = _K4Search_CreateFwdArcLists;
141
context->functions.fpCreateDFSTreeEmbedding = _K4Search_CreateDFSTreeEmbedding;
142
context->functions.fpEmbedBackEdgeToDescendant = _K4Search_EmbedBackEdgeToDescendant;
143
context->functions.fpMarkDFSPath = _K4Search_MarkDFSPath;
144
context->functions.fpHandleBlockedEmbedIteration = _K4Search_HandleBlockedEmbedIteration;
145
context->functions.fpHandleBlockedDescendantBicomp = _K4Search_HandleBlockedDescendantBicomp;
146
context->functions.fpEmbedPostprocess = _K4Search_EmbedPostprocess;
147
context->functions.fpCheckEmbeddingIntegrity = _K4Search_CheckEmbeddingIntegrity;
148
context->functions.fpCheckObstructionIntegrity = _K4Search_CheckObstructionIntegrity;
149
150
context->functions.fpInitGraphNode = _K4Search_InitGraphNode;
151
context->functions.fpInitVertexRec = _K4Search_InitVertexRec;
152
153
context->functions.fpInitGraph = _K4Search_InitGraph;
154
context->functions.fpReinitializeGraph = _K4Search_ReinitializeGraph;
155
context->functions.fpEnsureArcCapacity = _K4Search_EnsureArcCapacity;
156
157
_K4Search_ClearStructures(context);
158
159
// Store the K33 search context, including the data structure and the
160
// function pointers, as an extension of the graph
161
if (gp_AddExtension(theGraph, &K4SEARCH_ID, (void *) context,
162
_K4Search_DupContext, _K4Search_FreeContext,
163
&context->functions) != OK)
164
{
165
_K4Search_FreeContext(context);
166
return NOTOK;
167
}
168
169
// Create the K33-specific structures if the size of the graph is known
170
// Attach functions are always invoked after gp_New(), but if a graph
171
// extension must be attached before gp_Read(), then the attachment
172
// also happens before gp_InitGraph(), which means N==0.
173
// However, sometimes a feature is attached after gp_InitGraph(), in
174
// which case N > 0
175
if (theGraph->N > 0)
176
{
177
if (_K4Search_CreateStructures(context) != OK ||
178
_K4Search_InitStructures(context) != OK)
179
{
180
_K4Search_FreeContext(context);
181
return NOTOK;
182
}
183
}
184
185
return OK;
186
}
187
188
/********************************************************************
189
gp_DetachK4Search()
190
********************************************************************/
191
192
int gp_DetachK4Search(graphP theGraph)
193
{
194
return gp_RemoveExtension(theGraph, K4SEARCH_ID);
195
}
196
197
/********************************************************************
198
_K4Search_ClearStructures()
199
********************************************************************/
200
201
void _K4Search_ClearStructures(K4SearchContext *context)
202
{
203
if (!context->initialized)
204
{
205
// Before initialization, the pointers are stray, not NULL
206
// Once NULL or allocated, free() or LCFree() can do the job
207
context->sortedDFSChildLists = NULL;
208
context->G = NULL;
209
context->V = NULL;
210
211
context->initialized = 1;
212
}
213
else
214
{
215
LCFree(&context->sortedDFSChildLists);
216
if (context->G != NULL)
217
{
218
free(context->G);
219
context->G = NULL;
220
}
221
if (context->V != NULL)
222
{
223
free(context->V);
224
context->V = NULL;
225
}
226
}
227
}
228
229
/********************************************************************
230
_K4Search_CreateStructures()
231
Create uninitialized structures for the vertex and graph node
232
levels, and initialized structures for the graph level
233
********************************************************************/
234
int _K4Search_CreateStructures(K4SearchContext *context)
235
{
236
int N = context->theGraph->N;
237
int Gsize = context->theGraph->edgeOffset + context->theGraph->arcCapacity;
238
239
if (N <= 0)
240
return NOTOK;
241
242
if ((context->sortedDFSChildLists = LCNew(context->theGraph->N)) == NULL ||
243
(context->G = (K4Search_GraphNodeP) malloc(Gsize*sizeof(K4Search_GraphNode))) == NULL ||
244
(context->V = (K4Search_VertexRecP) malloc(N*sizeof(K4Search_VertexRec))) == NULL
245
)
246
{
247
return NOTOK;
248
}
249
250
return OK;
251
}
252
253
/********************************************************************
254
_K4Search_InitStructures()
255
********************************************************************/
256
int _K4Search_InitStructures(K4SearchContext *context)
257
{
258
int I, N = context->theGraph->N;
259
int Gsize = context->theGraph->edgeOffset + context->theGraph->arcCapacity;
260
261
if (N <= 0)
262
return OK;
263
264
for (I = 0; I < Gsize; I++)
265
_InitK4SearchGraphNode(context, I);
266
267
for (I = 0; I < N; I++)
268
_InitK4SearchVertexRec(context, I);
269
270
return OK;
271
}
272
273
/********************************************************************
274
********************************************************************/
275
276
int _K4Search_InitGraph(graphP theGraph, int N)
277
{
278
K4SearchContext *context = NULL;
279
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
280
281
if (context == NULL)
282
return NOTOK;
283
{
284
theGraph->N = N;
285
theGraph->edgeOffset = 2*N;
286
if (theGraph->arcCapacity == 0)
287
theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N;
288
289
if (_K4Search_CreateStructures(context) != OK)
290
return NOTOK;
291
292
// This call initializes the base graph structures, but it also
293
// initializes the custom graphnode and vertex level structures
294
// due to the overloads of InitGraphNode and InitVertexRec
295
context->functions.fpInitGraph(theGraph, N);
296
}
297
298
return OK;
299
}
300
301
/********************************************************************
302
********************************************************************/
303
304
void _K4Search_ReinitializeGraph(graphP theGraph)
305
{
306
K4SearchContext *context = NULL;
307
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
308
309
if (context != NULL)
310
{
311
// Reinitialization can go much faster if the underlying
312
// init graph node and vertex rec functions are called,
313
// rather than the overloads of this module, because it
314
// avoids lots of unnecessary gp_FindExtension() calls.
315
if (theGraph->functions.fpInitGraphNode == _K4Search_InitGraphNode &&
316
theGraph->functions.fpInitVertexRec == _K4Search_InitVertexRec)
317
{
318
// Restore the graph function pointers
319
theGraph->functions.fpInitGraphNode = context->functions.fpInitGraphNode;
320
theGraph->functions.fpInitVertexRec = context->functions.fpInitVertexRec;
321
322
// Reinitialize the graph
323
context->functions.fpReinitializeGraph(theGraph);
324
325
// Restore the function pointers that attach this feature
326
theGraph->functions.fpInitGraphNode = _K4Search_InitGraphNode;
327
theGraph->functions.fpInitVertexRec = _K4Search_InitVertexRec;
328
329
// Do the reinitialization that is specific to this module
330
_K4Search_InitStructures(context);
331
LCReset(context->sortedDFSChildLists);
332
}
333
334
// If optimization is not possible, then just stick with what works.
335
// Reinitialize the graph-level structure and then invoke the
336
// reinitialize function.
337
else
338
{
339
LCReset(context->sortedDFSChildLists);
340
341
// The underlying function fpReinitializeGraph() implicitly initializes the K33
342
// structures due to the overloads of fpInitGraphNode() and fpInitVertexRec().
343
// It just does so less efficiently because each invocation of InitGraphNode
344
// and InitVertexRec has to look up the extension again.
345
//// _K4Search_InitStructures(context);
346
context->functions.fpReinitializeGraph(theGraph);
347
}
348
}
349
}
350
351
/********************************************************************
352
The current implementation does not support an increase of arc
353
(edge record) capacity once the extension is attached to the graph
354
data structure. This is only due to not being necessary to support.
355
For now, it is easy to ensure the correct capacity before attaching
356
the extension, but support could be added later if there is some
357
reason to do so.
358
********************************************************************/
359
360
int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)
361
{
362
return NOTOK;
363
}
364
365
/********************************************************************
366
_K4Search_DupContext()
367
********************************************************************/
368
369
void *_K4Search_DupContext(void *pContext, void *theGraph)
370
{
371
K4SearchContext *context = (K4SearchContext *) pContext;
372
K4SearchContext *newContext = (K4SearchContext *) malloc(sizeof(K4SearchContext));
373
374
if (newContext != NULL)
375
{
376
int N = ((graphP) theGraph)->N;
377
int Gsize = ((graphP) theGraph)->edgeOffset + ((graphP) theGraph)->arcCapacity;
378
379
*newContext = *context;
380
381
newContext->theGraph = (graphP) theGraph;
382
383
newContext->initialized = 0;
384
_K4Search_ClearStructures(newContext);
385
if (N > 0)
386
{
387
if (_K4Search_CreateStructures(newContext) != OK)
388
{
389
_K4Search_FreeContext(newContext);
390
return NULL;
391
}
392
393
LCCopy(newContext->sortedDFSChildLists, context->sortedDFSChildLists);
394
memcpy(newContext->G, context->G, Gsize*sizeof(K4Search_GraphNode));
395
memcpy(newContext->V, context->V, N*sizeof(K4Search_VertexRec));
396
}
397
}
398
399
return newContext;
400
}
401
402
/********************************************************************
403
_K4Search_FreeContext()
404
********************************************************************/
405
406
void _K4Search_FreeContext(void *pContext)
407
{
408
K4SearchContext *context = (K4SearchContext *) pContext;
409
410
_K4Search_ClearStructures(context);
411
free(pContext);
412
}
413
414
/********************************************************************
415
_K4Search_CreateFwdArcLists()
416
417
Puts the forward arcs (back edges from a vertex to its descendants)
418
into a circular list indicated by the fwdArcList member, a task
419
simplified by the fact that they have already been placed in
420
succession at the end of the adjacency list.
421
422
For K4 search, the forward edges must be sorted by DFS number of
423
the descendant endpoint. The sort is linear time, but it is a little
424
slower, so we avoid this cost for the other planarity-related algorithms.
425
426
Returns OK on success, NOTOK on internal code failure
427
********************************************************************/
428
429
int _K4Search_CreateFwdArcLists(graphP theGraph)
430
{
431
K4SearchContext *context = NULL;
432
433
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
434
if (context == NULL)
435
return NOTOK;
436
437
// For isolating a K_4 homeomorph, we need the forward edges
438
// of each vertex to be in sorted order by DFI of descendants.
439
// Otherwise we just drop through to the normal processing...
440
441
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
442
{
443
int I, Jcur, Jnext, ancestor;
444
445
// for each vertex v in order, we follow each of its back edges
446
// to the twin forward edge in an ancestor u, then we move
447
// the forward edge to the fwdArcList of u. Since this loop
448
// processes vertices in order, the fwdArcList of each vertex
449
// will be in order by the neighbor indicated by the forward edges.
450
451
for (I=0; I < theGraph->N; I++)
452
{
453
// Skip this vertex if it has no edges
454
Jnext = gp_GetLastArc(theGraph, I);
455
if (!gp_IsArc(theGraph, Jnext))
456
continue;
457
458
// Skip the forward edges, which are in succession at the
459
// end of the arc list (last and its predecessors)
460
while (theGraph->G[Jnext].type == EDGE_FORWARD)
461
Jnext = gp_GetPrevArc(theGraph, Jnext);
462
463
// Now we want to put all the back arcs in a backArcList, too.
464
// Since we've already skipped past the forward arcs, we continue
465
// with the predecessor arcs until we either run out of arcs or
466
// we find a DFS child arc (the DFS child arcs are in succession
467
// at the beginning of the arc list, so when a child arc is
468
// encountered in the predecessor direction, then there won't be
469
// any more back arcs.
470
while (gp_IsArc(theGraph, Jnext) &&
471
theGraph->G[Jnext].type != EDGE_DFSCHILD)
472
{
473
Jcur = Jnext;
474
Jnext = gp_GetPrevArc(theGraph, Jnext);
475
476
if (theGraph->G[Jcur].type == EDGE_BACK)
477
{
478
// Remove the back arc from I's adjacency list
479
gp_DetachArc(theGraph, Jcur);
480
gp_SetPrevArc(theGraph, Jcur, NIL);
481
gp_SetNextArc(theGraph, Jcur, NIL);
482
483
// Determine the ancestor of vertex I to which Jcur connects
484
ancestor = theGraph->G[Jcur].v;
485
486
// Go to the forward arc in the ancestor
487
Jcur = gp_GetTwinArc(theGraph, Jcur);
488
489
// Remove the forward arc from the ancestor's adjacency list
490
gp_DetachArc(theGraph, Jcur);
491
492
// Add the forward arc to the end of the fwdArcList.
493
if (theGraph->V[ancestor].fwdArcList == NIL)
494
{
495
theGraph->V[ancestor].fwdArcList = Jcur;
496
gp_SetPrevArc(theGraph, Jcur, Jcur);
497
gp_SetNextArc(theGraph, Jcur, Jcur);
498
}
499
else
500
{
501
gp_AttachArc(theGraph, NIL, theGraph->V[ancestor].fwdArcList, 1, Jcur);
502
}
503
}
504
}
505
}
506
507
// Since the fwdArcLists have been created, we do not fall through
508
// to run the superclass implementation
509
return OK;
510
}
511
512
// If we're not actually running a K4 search, then we just run the
513
// superclass implementation
514
return context->functions.fpCreateFwdArcLists(theGraph);
515
}
516
517
/********************************************************************
518
_K4Search_CreateDFSTreeEmbedding()
519
520
This function overloads the basic planarity version in the manner
521
explained below. Once the extra behavior is performed, the basic
522
planarity version is invoked.
523
524
First, the sortedDFSChildList of each vertex is computed. Each vertex
525
receives the list of its DFS children sorted by their DFS numbers.
526
This is linear time overall. The core planarity/outerplanarity
527
algorithm computes a different list of the DFS children, the
528
separatedDFSChildList, in which the DFS children are stored in order
529
of their lowpoint values.
530
531
Second, the p2dFwdArcCount of each vertex is computed. This is the
532
number of forward arcs from the DFS parent of the vertex to DFS
533
descendants of the vertex. This is computed based on a simultaneous
534
traversal through the sortedDFSChildList and the sorted fwdArcList.
535
536
Third, the subtree value of each forward arc (V, D) is determined. This
537
value indicates the DFS child C of V whose DFS subtree contains the DFS
538
descendant endpoint D of the forward arc. This can be computed during
539
the setting of the p2dFwdArcCount values.
540
541
Each DFS child is listed in DFI order in V[I].sortedDFSChildList.
542
In V[I].fwdArcList, the forward arcs of all back edges are in order
543
by DFI of the descendant endpoint of the edge.
544
545
DFS descendants have a higher DFI than ancestors, so given two
546
successive children C1 and C2, if a forward arc leads to a
547
vertex D such that DFI(C1) < DFI(D) < DFI(C2), then the
548
forward arc contributes to the count of C1 and has C1 as subtree.
549
********************************************************************/
550
551
void _K4Search_CreateDFSTreeEmbedding(graphP theGraph)
552
{
553
K4SearchContext *context = NULL;
554
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
555
556
if (context != NULL)
557
{
558
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
559
{
560
int I, J, C1, C2, D, e;
561
int N = theGraph->N;
562
563
// First compute the sortedDFSChildList of each vertex
564
for (I=0; I<N; I++)
565
{
566
J = gp_GetFirstArc(theGraph, I);
567
568
// If a vertex has any DFS children, the edges
569
// to them are stored in descending order of
570
// the DFI's along the successor arc pointers, so
571
// we traverse them and prepend each to the
572
// ascending order sortedDFSChildList
573
while (theGraph->G[J].type == EDGE_DFSCHILD)
574
{
575
context->V[I].sortedDFSChildList =
576
LCPrepend(context->sortedDFSChildLists,
577
context->V[I].sortedDFSChildList,
578
theGraph->G[J].v);
579
580
J = gp_GetNextArc(theGraph, J);
581
}
582
}
583
584
// Next compute the p2dFwdArcCount of each vertex and the
585
// subtree of each forward arc.
586
for (I=0; I<N; I++)
587
{
588
// For each DFS child of the vertex I, ...
589
C1 = context->V[I].sortedDFSChildList;
590
e = theGraph->V[I].fwdArcList;
591
while (C1 != NIL && gp_IsArc(theGraph, e))
592
{
593
// Get the next higher numbered child C2
594
C2 = LCGetNext(context->sortedDFSChildLists,
595
context->V[I].sortedDFSChildList, C1);
596
597
// If there is a next child C2, then we can restrict attention
598
// to the forward arcs with DFI less than C2
599
if (C2 != NIL)
600
{
601
D = theGraph->G[e].v;
602
while (D < C2)
603
{
604
context->V[C1].p2dFwdArcCount++;
605
context->G[e].subtree = C1;
606
607
// Go to the next forward arc
608
e = gp_GetNextArc(theGraph, e);
609
if (e == theGraph->V[I].fwdArcList)
610
{
611
e = NIL;
612
break;
613
}
614
D = theGraph->G[e].v;
615
}
616
}
617
618
// If C1 is the last DFS child (C2==NIL), then all remaining
619
// forward edges must connect to descendants of C1.
620
else
621
{
622
while (gp_IsArc(theGraph, e))
623
{
624
context->V[C1].p2dFwdArcCount++;
625
context->G[e].subtree = C1;
626
627
// Go to the next forward arc
628
e = gp_GetNextArc(theGraph, e);
629
if (e == theGraph->V[I].fwdArcList)
630
e = NIL;
631
}
632
}
633
634
// Move the DFS child context to C2
635
C1 = C2;
636
}
637
}
638
}
639
640
// Invoke the superclass version of the function
641
// Each DFS tree child arc is moved to the root copy of the vertex
642
context->functions.fpCreateDFSTreeEmbedding(theGraph);
643
}
644
}
645
646
/********************************************************************
647
_K4Search_EmbedBackEdgeToDescendant()
648
649
The forward and back arcs of the cycle edge are embedded by the planarity
650
version of this function.
651
However, for K_4 subgraph homeomorphism, we also maintain a forward
652
arc counter in a DFS child C of each vertex V to indicate how many
653
forward arcs there are from V to descendants of C. Each forward arc
654
has an indicator, 'subtree', of C. When we embed the edge, we decrement
655
the counter so that when the WalkDown resolves as much pertinence as
656
possible along the external face of the bicomp rooted by R=C+N, then
657
we can easily determine whether there is more unresolved pertinence
658
by testing whether the forward arc count has dropped to zero.
659
If not, then we either find a K4 or perform a reduction that enables
660
the WalkDown to make more progress when reinvoked.
661
********************************************************************/
662
663
void _K4Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink)
664
{
665
K4SearchContext *context = NULL;
666
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
667
668
if (context != NULL)
669
{
670
// K4 search may have been attached, but not enabled
671
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
672
{
673
int fwdArc = theGraph->V[W].adjacentTo;
674
context->V[context->G[fwdArc].subtree].p2dFwdArcCount--;
675
}
676
677
// Invoke the superclass version of the function
678
context->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink);
679
}
680
}
681
682
/********************************************************************
683
********************************************************************/
684
685
void _K4Search_InitGraphNode(graphP theGraph, int I)
686
{
687
K4SearchContext *context = NULL;
688
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
689
690
if (context != NULL)
691
{
692
context->functions.fpInitGraphNode(theGraph, I);
693
_InitK4SearchGraphNode(context, I);
694
}
695
}
696
697
void _InitK4SearchGraphNode(K4SearchContext *context, int I)
698
{
699
context->G[I].pathConnector = NIL;
700
context->G[I].subtree = NIL;
701
}
702
703
/********************************************************************
704
********************************************************************/
705
706
void _K4Search_InitVertexRec(graphP theGraph, int I)
707
{
708
K4SearchContext *context = NULL;
709
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
710
711
if (context != NULL)
712
{
713
context->functions.fpInitVertexRec(theGraph, I);
714
_InitK4SearchVertexRec(context, I);
715
}
716
}
717
718
void _InitK4SearchVertexRec(K4SearchContext *context, int I)
719
{
720
context->V[I].p2dFwdArcCount = 0;
721
context->V[I].sortedDFSChildList = NIL;
722
}
723
724
/********************************************************************
725
// This function used to be implemented by going to each
726
// vertex, asking for its DFS parent, then finding the
727
// edge that lead to that DFS parent and marking it.
728
// However, the K4 search performs certain bicomp reductions
729
// that are required to preserve the essential structure of
730
// the DFS tree. As a result, sometimes a DFSParent has been
731
// reduced out of the graph, but the tree edge leads to the nearest
732
// ancestor still in the graph. So, when we want to leave a vertex,
733
// we search for the DFS tree edge to the "parent" (nearest ancestor),
734
// then we mark the edge and use it to go up to the "parent".
735
********************************************************************/
736
737
int _K4Search_MarkDFSPath(graphP theGraph, int ancestor, int descendant)
738
{
739
int J, parent, N;
740
741
N = theGraph->N;
742
743
/* If we are marking from a root vertex upward, then go up to the parent
744
copy before starting the loop */
745
746
if (descendant >= N)
747
descendant = theGraph->V[descendant-N].DFSParent;
748
749
/* Mark the lowest vertex (the one with the highest number). */
750
751
theGraph->G[descendant].visited = 1;
752
753
/* Mark all ancestors of the lowest vertex, and the edges used to reach
754
them, up to the given ancestor vertex. */
755
756
while (descendant != ancestor)
757
{
758
if (descendant == NIL)
759
return NOTOK;
760
761
/* If we are at a bicomp root, then ascend to its parent copy and
762
mark it as visited. */
763
764
if (descendant >= N)
765
{
766
parent = theGraph->V[descendant-N].DFSParent;
767
}
768
769
/* If we are on a regular, non-virtual vertex then get the edge to
770
the parent, mark the edge, then fall through to the code
771
that marks the parent vertex. */
772
else
773
{
774
775
/* Scan the edges for the one marked as the DFS parent */
776
777
parent = NIL;
778
J = gp_GetFirstArc(theGraph, descendant);
779
while (gp_IsArc(theGraph, J))
780
{
781
if (theGraph->G[J].type == EDGE_DFSPARENT)
782
{
783
parent = theGraph->G[J].v;
784
break;
785
}
786
J = gp_GetNextArc(theGraph, J);
787
}
788
789
/* If the desired edge was not found, then the data structure is
790
corrupt, so bail out. */
791
792
if (parent == NIL)
793
return NOTOK;
794
795
/* Mark the edge */
796
797
theGraph->G[J].visited = 1;
798
theGraph->G[gp_GetTwinArc(theGraph, J)].visited = 1;
799
}
800
801
/* Mark the parent, then hop to the parent and reiterate */
802
803
theGraph->G[parent].visited = 1;
804
descendant = parent;
805
}
806
807
return OK;
808
}
809
810
/********************************************************************
811
* This function is called if the outerplanarity algorithm fails to
812
* embed all back edges for a vertex I. This means that an obstruction
813
* to outerplanarity has occurred, so we determine if it is a subgraph
814
* homeomorphic to K4. If so, then NONEMBEDDABLE is returned. If not,
815
* then a reduction is performed that unobstructs outerplanarity and
816
* OK is returned, which allows the outerplanarity algorithm to
817
* proceed with iteration I-1 (or to stop if I==0).
818
********************************************************************/
819
820
int _K4Search_HandleBlockedEmbedIteration(graphP theGraph, int I)
821
{
822
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
823
{
824
// If the fwdArcList is empty, then the K4 was already isolated
825
// by _K4Search_HandleBlockedDescendantBicomp(), and we just
826
// return the NONEMBEDDABLE result in order to stop the embedding
827
// iteration loop.
828
if (theGraph->V[I].fwdArcList == NIL)
829
return NONEMBEDDABLE;
830
831
return _SearchForK4InBicomps(theGraph, I);
832
}
833
else
834
{
835
K4SearchContext *context = NULL;
836
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
837
838
if (context != NULL)
839
{
840
return context->functions.fpHandleBlockedEmbedIteration(theGraph, I);
841
}
842
}
843
844
return NOTOK;
845
}
846
847
/********************************************************************
848
This function is called when outerplanarity obstruction minor A is
849
encountered by the WalkDown. In the implementation for the core
850
planarity/outerplanarity algorithm, this method simply pushes the
851
blocked bicomp root onto the stack and returns NONEMBEDDABLE, which
852
causes the WalkDown to terminate. The embed postprocessing would
853
then go on to isolate the obstruction.
854
855
However, outerplanarity obstruction minor A corresponds to a K_{2,3}
856
homeomorph. This method invokes a search for a K_4 homeomorph that
857
may be entangled with the K_{2,3} homeomorph. If an entangled K_4
858
homeomorph is found, then _SearchForK4() returns NONEMBEDDABLE, which
859
causes the WalkDown to terminate as above. This is correct since a
860
K_4 homeomorph has been found and isolated, and the K4Search overload
861
of EmbedPostprocess() does no additional work.
862
863
On the other hand, if minor A is found but there is no entangled K_4
864
homeomorph, then the blocked descendant was reduced to a single edge
865
so that it no longer obstructs outerplanarity. Then, OK was returned
866
to indicate that the WalkDown should proceed. This function then
867
sets the vertex W and directional information that must be returned
868
so that WalkDown can proceed.
869
870
Returns OK to proceed with WalkDown at W,
871
NONEMBEDDABLE to terminate WalkDown of Root Vertex
872
NOTOK for internal error
873
********************************************************************/
874
875
int _K4Search_HandleBlockedDescendantBicomp(graphP theGraph, int I, int RootVertex, int R, int *pRout, int *pW, int *pWPrevLink)
876
{
877
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
878
{
879
int RetVal = _SearchForK4InBicomp(theGraph, NULL, I, R);
880
881
// On internal error (NOTOK) or K4 found (NONEMBEDDABLE), we return.
882
if (RetVal != OK)
883
return RetVal;
884
885
// Since the bicomp rooted by R is now a singleton edge, either direction
886
// out of R and into W can be selected, as long as they are consistent
887
// We just choose the settings associated with selecting W as the next
888
// vertex from R on the external face.
889
*pRout = 0;
890
*pWPrevLink = 1;
891
*pW = _GetNextVertexOnExternalFace(theGraph, R, pWPrevLink);
892
893
// Now return OK so the Walkdown can continue at W (i.e. *pW)
894
return OK;
895
}
896
else
897
{
898
K4SearchContext *context = NULL;
899
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
900
901
if (context != NULL)
902
{
903
return context->functions.fpHandleBlockedDescendantBicomp(theGraph, I, RootVertex, R, pRout, pW, pWPrevLink);
904
}
905
}
906
907
return NOTOK;
908
}
909
910
/********************************************************************
911
********************************************************************/
912
913
int _K4Search_EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult)
914
{
915
// For K4 search, we just return the edge embedding result because the
916
// search result has been obtained already.
917
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
918
{
919
return edgeEmbeddingResult;
920
}
921
922
// When not searching for K4, we let the superclass do the work
923
else
924
{
925
K4SearchContext *context = NULL;
926
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
927
928
if (context != NULL)
929
{
930
return context->functions.fpEmbedPostprocess(theGraph, I, edgeEmbeddingResult);
931
}
932
}
933
934
return NOTOK;
935
}
936
937
/********************************************************************
938
********************************************************************/
939
940
int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)
941
{
942
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
943
{
944
return OK;
945
}
946
947
// When not searching for K4, we let the superclass do the work
948
else
949
{
950
K4SearchContext *context = NULL;
951
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
952
953
if (context != NULL)
954
{
955
return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph);
956
}
957
}
958
959
return NOTOK;
960
}
961
962
/********************************************************************
963
********************************************************************/
964
965
int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)
966
{
967
// When searching for K4, we ensure that theGraph is a subgraph of
968
// the original graph and that it contains a K4 homeomorph
969
if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4)
970
{
971
int degrees[4], imageVerts[4];
972
973
if (_TestSubgraph(theGraph, origGraph) != TRUE)
974
return NOTOK;
975
976
if (_getImageVertices(theGraph, degrees, 3, imageVerts, 4) != OK)
977
return NOTOK;
978
979
if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE)
980
{
981
return OK;
982
}
983
984
return NOTOK;
985
}
986
987
// When not searching for K4, we let the superclass do the work
988
else
989
{
990
K4SearchContext *context = NULL;
991
gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context);
992
993
if (context != NULL)
994
{
995
return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph);
996
}
997
}
998
999
return NOTOK;
1000
}
1001
1002