From 436d292ea602700d5e4fd2ee29a300f96a16ab95 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 11:43:09 +0200 Subject: [PATCH 1/7] Update to Planarity v4.0.0.0 --- Makefile.in | 53 ++++--- configure.ac | 2 +- src/planar.c | 389 ++++++++++++++++++++++++++------------------------- 3 files changed, 231 insertions(+), 213 deletions(-) diff --git a/Makefile.in b/Makefile.in index 127623632..4ecbf2f44 100644 --- a/Makefile.in +++ b/Makefile.in @@ -45,28 +45,37 @@ endif ifdef WITH_INCLUDED_PLANARITY - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphDFSUtils.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphDrawPlanar.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphDrawPlanar_Extensions.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphEmbed.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphExtensions.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphIO.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphIsolator.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphK23Search.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphK23Search_Extensions.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphK33Search.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphK33Search_Extensions.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphK4Search.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphK4Search_Extensions.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphNonplanar.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphOuterplanarObstruction.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphTests.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphUtils.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/listcoll.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/planarityRandomGraphs.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/planaritySpecificGraph.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/planarityUtils.c - KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/stack.c + + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/graphDFSUtils.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/graphUtils.c + + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/extensionSystem/graphExtensions.c + + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/homeomorphSearch/graphK23Search.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/homeomorphSearch/graphK33Search.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/homeomorphSearch/graphK4Search.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c + + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/io/g6-api-utilities.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/io/g6-read-iterator.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/io/g6-write-iterator.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/io/graphIO.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/io/strOrFile.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/io/strbuf.c + + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/lowLevelUtils/apiutils.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/lowLevelUtils/listcoll.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/lowLevelUtils/stack.c + + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphDrawPlanar.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphEmbed.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphIsolator.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphNonplanar.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphOuterplanarObstruction.c + KEXT_SOURCES += $(PLANARITY_SUITE_DIR)/c/graphLib/planarityRelated/graphTests.c KEXT_CFLAGS += -I$(PLANARITY_SUITE_DIR) else diff --git a/configure.ac b/configure.ac index fcabd1fb3..56b4aea74 100644 --- a/configure.ac +++ b/configure.ac @@ -94,7 +94,7 @@ AS_IF([test "x$enable_compile_warnings" != "xno"], AX_CHECK_PLANARITY if test "x$with_external_planarity" = xno ; then - PLANARITY_SUITE_DIR=extern/edge-addition-planarity-suite-Version_3.0.1.0 + PLANARITY_SUITE_DIR=extern/edge-addition-planarity-suite-Version_4.0.0.0 AC_SUBST(PLANARITY_SUITE_DIR) fi diff --git a/src/planar.c b/src/planar.c index 1cdf94a76..7c897f6f7 100644 --- a/src/planar.c +++ b/src/planar.c @@ -12,19 +12,19 @@ #include "planar.h" // C headers -#include // for INT_MAX -#include // for true and false +#include // for INT_MAX +#include // for true and false // Digraphs package headers -#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY -#include "digraphs-debug.h" // for DIGRAPHS_ASSERT -#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . -#include "gap-includes.h" // for Fail, False, True +#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . +#include "gap-includes.h" // for Fail, False, True // edge-addition-planarity-suite headers #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored \ +#pragma clang diagnostic ignored \ "-Wincompatible-pointer-types-discards-qualifiers" #pragma clang diagnostic ignored "-Wstrict-prototypes" #elif defined(__GNUC__) @@ -33,15 +33,9 @@ #pragma GCC diagnostic ignored "-Wswitch-default" #endif #ifdef DIGRAPHS_WITH_INCLUDED_PLANARITY -#include "c/graph.h" -#include "c/graphK23Search.h" -#include "c/graphK33Search.h" -#include "c/graphK4Search.h" +#include "c/graphLib.h" #else -#include "planarity/graph.h" -#include "planarity/graphK23Search.h" -#include "planarity/graphK33Search.h" -#include "planarity/graphK4Search.h" +#include "planarity/graphLib.h" #endif #if defined(__clang__) #pragma clang diagnostic pop @@ -56,222 +50,237 @@ Obj trivial_planarity_output(Int V, bool krtwsk); Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk); // GAP level functions -Obj FuncIS_PLANAR(Obj self, Obj digraph) { - return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); +Obj FuncIS_PLANAR(Obj self, Obj digraph) +{ + return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); } -Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) { - return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); +Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) +{ + return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); } -Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } // The implementation of the main functions in this file. -Obj trivial_planarity_output(Int V, bool krtwsk) { - Obj res; - if (krtwsk) { - Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); - SET_LEN_PLIST(subgraph, V); - for (int i = 1; i <= V; ++i) { - Obj list = NEW_PLIST_IMM(T_PLIST, 0); - SET_LEN_PLIST(list, 0); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); +Obj trivial_planarity_output(Int V, bool krtwsk) +{ + Obj res; + if (krtwsk) { + Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); + SET_LEN_PLIST(subgraph, V); + for (int i = 1; i <= V; ++i) { + Obj list = NEW_PLIST_IMM(T_PLIST, 0); + SET_LEN_PLIST(list, 0); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); + } + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, True); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } + else { + res = True; } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, True); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } else { - res = True; - } - return res; + return res; } // This function only accepts digraphs without multiple edges -Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { - DIGRAPHS_ASSERT(flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR - || flags == EMBEDFLAGS_SEARCHFORK23 - || flags == EMBEDFLAGS_SEARCHFORK4 - || flags == EMBEDFLAGS_SEARCHFORK33); +Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) +{ + DIGRAPHS_ASSERT( + flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR || + flags == EMBEDFLAGS_SEARCHFORK23 || flags == EMBEDFLAGS_SEARCHFORK4 || + flags == EMBEDFLAGS_SEARCHFORK33); - if (CALL_1ARGS(IsDigraph, digraph) != True) { - ErrorQuit("Digraphs: boyers_planarity_check (C): the 1st argument must be " - "a digraph, not %s", - (Int) TNAM_OBJ(digraph), - 0L); - } - if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { - ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); - } + if (CALL_1ARGS(IsDigraph, digraph) != True) { + ErrorQuit( + "Digraphs: boyers_planarity_check (C): the 1st argument must be " + "a digraph, not %s", + (Int)TNAM_OBJ(digraph), 0L); + } + if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { + ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); + } - Int V = DigraphNrVertices(digraph); - if (V == 0) { - return trivial_planarity_output(0, krtwsk); - } + Int V = DigraphNrVertices(digraph); + if (V == 0) { + return trivial_planarity_output(0, krtwsk); + } - Int E = DigraphNrAdjacenciesWithoutLoops(digraph); - if (E == 0) { - return trivial_planarity_output(V, krtwsk); - } - if (V > INT_MAX) { - // Cannot currently test this, it might always be true, depending on the - // definition of Int. - ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " - "nodes is %d, found %d", - INT_MAX, - V); - return 0L; - } else if (2 * E > INT_MAX) { - // Cannot currently test this - ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " - "edges is %d, found %d", - INT_MAX / 2, - E); - return 0L; - } + Int E = DigraphNrAdjacenciesWithoutLoops(digraph); + if (E == 0) { + return trivial_planarity_output(V, krtwsk); + } + if (V > INT_MAX) { + // Cannot currently test this, it might always be true, depending on + // the definition of Int. + ErrorQuit( + "Digraphs: boyers_planarity_check (C): the maximum number of " + "nodes is %d, found %d", + INT_MAX, V); + return 0L; + } + else if (2 * E > INT_MAX) { + // Cannot currently test this + ErrorQuit( + "Digraphs: boyers_planarity_check (C): the maximum number of " + "edges is %d, found %d", + INT_MAX / 2, E); + return 0L; + } - graphP theGraph = gp_New(); + graphP theGraph = gp_New(); - if (gp_InitGraph(theGraph, V) != OK) { - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", - 0L, - 0L); - return 0L; - } else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of edges!", - 0L, - 0L); - return 0L; - } + if (gp_InitGraph(theGraph, V) != OK) { + gp_Free(&theGraph); + ErrorQuit( + "Digraphs: boyers_planarity_check (C): invalid number of nodes!", + 0L, 0L); + return 0L; + } + else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { + gp_Free(&theGraph); + ErrorQuit( + "Digraphs: boyers_planarity_check (C): invalid number of edges!", + 0L, 0L); + return 0L; + } - switch (flags) { + switch (flags) { case EMBEDFLAGS_SEARCHFORK33: - gp_AttachK33Search(theGraph); - break; + gp_AttachK33Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK23: - gp_AttachK23Search(theGraph); - break; + gp_AttachK23Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK4: - gp_AttachK4Search(theGraph); - break; + gp_AttachK4Search(theGraph); + break; default: - break; - } + break; + } - int status; - Obj const out = FuncOutNeighbours(0L, digraph); + int status; + Obj const out = FuncOutNeighbours(0L, digraph); - // Construct the antisymmetric digraph with no loops - for (Int v = 1; v <= LEN_LIST(out); ++v) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); - gp_SetVertexIndex(theGraph, v, v); - Obj const out_v = ELM_LIST(out, v); - for (Int w = 1; w <= LEN_LIST(out_v); ++w) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); - Int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v < u - || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) - == False) { - status = gp_AddEdge(theGraph, v, 0, u, 0); - if (status != OK) { - // Cannot currently test this, i.e. it shouldn't happen (and - // currently there is no example where it does happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): internal error, " - "can't add edge from %d to %d", - (Int) v, - (Int) u); - return 0L; + // Construct the antisymmetric digraph with no loops + for (Int v = 1; v <= LEN_LIST(out); ++v) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); + gp_SetVertexIndex(theGraph, v, v); + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), + INTOBJ_INT(v)) == False) { + status = gp_AddEdge(theGraph, v, 0, u, 0); + if (status != OK) { + // Cannot currently test this, i.e. it shouldn't happen + // (and currently there is no example where it does + // happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): " + "internal error, " + "can't add edge from %d to %d", + (Int)v, (Int)u); + return 0L; + } + } } - } } - } - status = gp_Embed(theGraph, flags); - if (status == NOTOK) { - // Cannot currently test this, i.e. it shouldn't happen (and - // currently there is no example where it does happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", 0L, 0L); - } + status = gp_Embed(theGraph, flags); + if (status == NOTOK) { + // Cannot currently test this, i.e. it shouldn't happen (and + // currently there is no example where it does happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", + 0L, 0L); + } - // Construct the return value - Obj res; - if (krtwsk) { - // Kuratowski subgraph isolator - gp_SortVertices(theGraph); - Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); - SET_LEN_PLIST(subgraph, theGraph->N); - for (int i = 1; i <= theGraph->N; ++i) { - int nr = 0; - Obj list = NEW_PLIST(T_PLIST, 0); - int j = theGraph->V[i].link[1]; - while (j) { - if (CALL_3ARGS(IsDigraphEdge, - digraph, - INTOBJ_INT((Int) i), - INTOBJ_INT((Int) theGraph->E[j].neighbor)) - == True) { - AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); + // Construct the return value + Obj res; + if (krtwsk) { + // Kuratowski subgraph isolator + gp_SortVertices(theGraph); + Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); + SET_LEN_PLIST(subgraph, theGraph->N); + for (int i = 1; i <= theGraph->N; ++i) { + int nr = 0; + Obj list = NEW_PLIST(T_PLIST, 0); + int j = theGraph->V[i].link[1]; + while (j) { + if (CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT((Int)i), + INTOBJ_INT((Int)theGraph->E[j].neighbor)) == + True) { + AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); + } + j = theGraph->E[j].link[1]; + } + if (nr == 0) { + SET_LEN_PLIST(list, 0); + } + MakeImmutable(list); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); } - j = theGraph->E[j].link[1]; - } - if (nr == 0) { - SET_LEN_PLIST(list, 0); - } - MakeImmutable(list); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } + else if (status == NONEMBEDDABLE) { + res = False; } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } else if (status == NONEMBEDDABLE) { - res = False; - } else { - res = True; - } - gp_Free(&theGraph); - return res; + else { + res = True; + } + gp_Free(&theGraph); + return res; } From e35d121e0018767c07c6ff68b2391bec37c75505 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 11:49:58 +0200 Subject: [PATCH 2/7] Add missing files --- .../.editorconfig | 10 + .../.gitattributes | 3 + .../.gitignore | 109 + .../Makefile.am | 93 + .../README.md | 73 + .../TestSupport/generate_graphs.bat | 20 + .../TestSupport/planarity_runner.bat | 27 + .../TestSupport/planaritytesting/__init__.py | 27 + .../edge_deletion_analysis.py | 1146 ++++++++ .../planaritytesting/g6_diff_finder.py | 617 ++++ .../g6_generation_and_comparison_driver.py | 1089 +++++++ .../TestSupport/planaritytesting/graph.py | 303 ++ .../graph_generation_orchestrator.py | 209 ++ .../planarity_leaks_config_manager.py | 200 ++ .../planarity_leaks_orchestrator.py | 987 +++++++ .../planarity_testAllGraphs_orchestrator.py | 333 +++ .../planarity_testAllGraphs_output_parsing.py | 261 ++ .../planaritytesting_utils.py | 185 ++ ...test_table_generation_with_numInvalidOK.py | 1168 ++++++++ .../planaritytesting/test_table_generator.py | 610 ++++ .../c/.gdbinit | 1 + .../c/graph.h | 28 + .../c/graphLib.h | 25 + .../extensionSystem/graphExtensions.c | 545 ++++ .../extensionSystem/graphExtensions.h | 37 + .../extensionSystem/graphExtensions.private.h | 35 + .../extensionSystem/graphFunctionTable.h | 66 + .../c/graphLib/graph.h | 141 + .../c/graphLib/graphDFSUtils.c | 457 +++ .../c/graphLib/graphLib.h | 59 + .../c/graphLib/graphStructures.h | 788 +++++ .../c/graphLib/graphUtils.c | 2563 +++++++++++++++++ .../homeomorphSearch/graphK23Search.c | 266 ++ .../homeomorphSearch/graphK23Search.h | 26 + .../homeomorphSearch/graphK23Search.private.h | 28 + .../graphK23Search_Extensions.c | 248 ++ .../homeomorphSearch/graphK33Search.c | 2007 +++++++++++++ .../homeomorphSearch/graphK33Search.h | 26 + .../homeomorphSearch/graphK33Search.private.h | 62 + .../graphK33Search_Extensions.c | 751 +++++ .../graphLib/homeomorphSearch/graphK4Search.c | 1490 ++++++++++ .../graphLib/homeomorphSearch/graphK4Search.h | 26 + .../homeomorphSearch/graphK4Search.private.h | 61 + .../graphK4Search_Extensions.c | 495 ++++ .../c/graphLib/io/g6-api-utilities.c | 41 + .../c/graphLib/io/g6-api-utilities.h | 24 + .../c/graphLib/io/g6-read-iterator.c | 728 +++++ .../c/graphLib/io/g6-read-iterator.h | 65 + .../c/graphLib/io/g6-write-iterator.c | 567 ++++ .../c/graphLib/io/g6-write-iterator.h | 69 + .../c/graphLib/io/graphIO.c | 1067 +++++++ .../c/graphLib/io/strOrFile.c | 718 +++++ .../c/graphLib/io/strOrFile.h | 61 + .../c/graphLib/io/strbuf.c | 247 ++ .../c/graphLib/io/strbuf.h | 67 + .../c/graphLib/lowLevelUtils/apiutils.c | 73 + .../c/graphLib/lowLevelUtils/apiutils.h | 40 + .../c/graphLib/lowLevelUtils/appconst.h | 103 + .../c/graphLib/lowLevelUtils/listcoll.c | 278 ++ .../c/graphLib/lowLevelUtils/listcoll.h | 122 + .../c/graphLib/lowLevelUtils/platformTime.h | 54 + .../c/graphLib/lowLevelUtils/stack.c | 224 ++ .../c/graphLib/lowLevelUtils/stack.h | 142 + .../planarityRelated/graphDrawPlanar.c | 1065 +++++++ .../planarityRelated/graphDrawPlanar.h | 29 + .../graphDrawPlanar.private.h | 91 + .../graphDrawPlanar_Extensions.c | 679 +++++ .../c/graphLib/planarityRelated/graphEmbed.c | 1373 +++++++++ .../graphLib/planarityRelated/graphIsolator.c | 833 ++++++ .../planarityRelated/graphNonplanar.c | 828 ++++++ .../graphOuterplanarObstruction.c | 196 ++ .../c/graphLib/planarityRelated/graphTests.c | 968 +++++++ .../c/planarityApp/planarity.1 | 130 + .../c/planarityApp/planarity.c | 30 + .../c/planarityApp/planarity.h | 76 + .../c/planarityApp/planarityCommandLine.c | 781 +++++ .../c/planarityApp/planarityHelp.c | 123 + .../c/planarityApp/planarityMenu.c | 216 ++ .../c/planarityApp/planarityRandomGraphs.c | 563 ++++ .../c/planarityApp/planaritySpecificGraph.c | 273 ++ .../c/planarityApp/planarityTestAllGraphs.c | 391 +++ .../c/planarityApp/planarityTransformGraph.c | 102 + .../c/planarityApp/planarityUtils.c | 666 +++++ .../c/samples/Makefile.am | 46 + .../c/samples/nauty_example.LEDA | 17 + .../configure.ac | 44 + .../.vscode/launch.json | 229 ++ .../.vscode/settings.json | 22 + .../devEnvSetupAndDefaults/.vscode/tasks.json | 314 ++ .../TestSupport/planaritytesting/.pylintrc | 645 +++++ .../planarity_leaks_config.ini | 35 + .../devEnvSetupAndDefaults/devEnvSetup.sh | 20 + .../libplanarity.pc.in | 11 + .../test-samples.sh.in | 11 + 94 files changed, 33198 insertions(+) create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/.editorconfig create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitattributes create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitignore create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/Makefile.am create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/README.md create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/generate_graphs.bat create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planarity_runner.bat create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/__init__.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/edge_deletion_analysis.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_diff_finder.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_generation_and_comparison_driver.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph_generation_orchestrator.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config_manager.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_orchestrator.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_orchestrator.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_output_parsing.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planaritytesting_utils.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generation_with_numInvalidOK.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generator.py create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/.gdbinit create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graph.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.private.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphFunctionTable.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graph.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphStructures.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.private.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.private.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.private.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/appconst.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/platformTime.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.private.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphOuterplanarObstruction.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.1 create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.h create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityCommandLine.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityHelp.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityMenu.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityRandomGraphs.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planaritySpecificGraph.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTestAllGraphs.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTransformGraph.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityUtils.c create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/Makefile.am create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/nauty_example.LEDA create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/configure.ac create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/launch.json create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/settings.json create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/tasks.json create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/.pylintrc create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config.ini create mode 100755 extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/devEnvSetup.sh create mode 100644 extern/edge-addition-planarity-suite-Version_4.0.0.0/libplanarity.pc.in create mode 100755 extern/edge-addition-planarity-suite-Version_4.0.0.0/test-samples.sh.in diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/.editorconfig b/extern/edge-addition-planarity-suite-Version_4.0.0.0/.editorconfig new file mode 100644 index 000000000..576fe73da --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/.editorconfig @@ -0,0 +1,10 @@ +# From https://stackoverflow.com/a/42136008 +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitattributes b/extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitattributes new file mode 100644 index 000000000..49f50d23b --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitattributes @@ -0,0 +1,3 @@ +# From https://stackoverflow.com/a/42136008 +# See also https://docs.github.com/en/get-started/git-basics/configuring-git-to-handle-line-endings +* text=auto eol=lf diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitignore b/extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitignore new file mode 100644 index 000000000..ff7f2d23e --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/.gitignore @@ -0,0 +1,109 @@ +# Directories +**/Release/ +**/Debug/ +/c/.settings/ + +# Ignore directories for output of Random Graphs +**/random/ +**/embedded/ +**/adjlist/ +**/obstructed/ +**/error/ + +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Linker output +*.ilk +*.map +*.exp + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Java +*.class +*.log +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# To ignore files generated by the make distribution process +*.tar.gz +**/.deps/ +/autom4te.cache/ +libtool +test-samples.sh +Makefile +config.status +config.guess* +config.sub* +depcomp +install-sh* +Makefile.in +missing +ltmain.sh +configure +configure~ +aclocal.m4 +compile +test-driver +planarity-* +planarity-*/* +libplanarity.pc +m4/ + +# Ignoring configuration for VSCode +/.vscode/ + +# Ignoring configuration for Eclipse project +.project +/c/.project +/c/.cproject + +## MacOS excludes +.DS_Store + +# Ignore binary produced by compiling on MacOS +planarity + +# Ignore planaritytesting Package default output directories, pycache, +# .pylintrc, and configuration file for leaks orchestrator +TestSupport/planaritytesting/__pycache__/ +TestSupport/results/ +TestSupport/g6_diff_finder_logs/ +TestSupport/planaritytesting/.pylintrc +TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config.ini diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/Makefile.am b/extern/edge-addition-planarity-suite-Version_4.0.0.0/Makefile.am new file mode 100644 index 000000000..796a5cf4b --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/Makefile.am @@ -0,0 +1,93 @@ +SUBDIRS = c/samples + +ACLOCAL_AMFLAGS = -I m4 + +lib_LTLIBRARIES = libplanarity.la + +libplanarity_la_SOURCES = \ + c/graphLib/graphDFSUtils.c \ + c/graphLib/graphUtils.c \ + c/graphLib/extensionSystem/graphExtensions.c \ + c/graphLib/homeomorphSearch/graphK23Search.c \ + c/graphLib/homeomorphSearch/graphK23Search_Extensions.c \ + c/graphLib/homeomorphSearch/graphK33Search.c \ + c/graphLib/homeomorphSearch/graphK33Search_Extensions.c \ + c/graphLib/homeomorphSearch/graphK4Search.c \ + c/graphLib/homeomorphSearch/graphK4Search_Extensions.c \ + c/graphLib/io/g6-api-utilities.c \ + c/graphLib/io/g6-read-iterator.c \ + c/graphLib/io/g6-write-iterator.c \ + c/graphLib/io/graphIO.c \ + c/graphLib/io/strbuf.c \ + c/graphLib/io/strOrFile.c \ + c/graphLib/lowLevelUtils/apiutils.c \ + c/graphLib/lowLevelUtils/listcoll.c \ + c/graphLib/lowLevelUtils/stack.c \ + c/graphLib/planarityRelated/graphDrawPlanar.c \ + c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c \ + c/graphLib/planarityRelated/graphEmbed.c \ + c/graphLib/planarityRelated/graphIsolator.c \ + c/graphLib/planarityRelated/graphNonplanar.c \ + c/graphLib/planarityRelated/graphOuterplanarObstruction.c \ + c/graphLib/planarityRelated/graphTests.c + +libplanarity_la_LDFLAGS = -no-undefined $(AM_LDFLAGS) -version-info @LT_CURRENT@:@LT_REVISION@:@LT_AGE@ + +# These pkgincludes are added without "nobase_" so that the files will be copied to the +# root of the planarity project includedir (i.e. excluding the c subdirectory) +pkginclude_HEADERS = \ + c/graph.h \ + c/graphLib.h + +# This is the full set of pkgincludes for which "nobase_" has been used to ensure that +# the directory structure is retained when installing the planarity headers. +nobase_pkginclude_HEADERS = \ + c/graphLib/graph.h \ + c/graphLib/graphLib.h \ + c/graphLib/graphStructures.h \ + c/graphLib/extensionSystem/graphExtensions.h \ + c/graphLib/extensionSystem/graphExtensions.private.h \ + c/graphLib/extensionSystem/graphFunctionTable.h \ + c/graphLib/homeomorphSearch/graphK23Search.h \ + c/graphLib/homeomorphSearch/graphK23Search.private.h \ + c/graphLib/homeomorphSearch/graphK33Search.h \ + c/graphLib/homeomorphSearch/graphK33Search.private.h \ + c/graphLib/homeomorphSearch/graphK4Search.h \ + c/graphLib/homeomorphSearch/graphK4Search.private.h \ + c/graphLib/io/g6-api-utilities.h \ + c/graphLib/io/g6-read-iterator.h \ + c/graphLib/io/g6-write-iterator.h \ + c/graphLib/io/strbuf.h \ + c/graphLib/io/strOrFile.h \ + c/graphLib/lowLevelUtils/apiutils.h \ + c/graphLib/lowLevelUtils/appconst.h \ + c/graphLib/lowLevelUtils/listcoll.h \ + c/graphLib/lowLevelUtils/platformTime.h \ + c/graphLib/lowLevelUtils/stack.h \ + c/graphLib/planarityRelated/graphDrawPlanar.h \ + c/graphLib/planarityRelated/graphDrawPlanar.private.h \ + c/planarityApp/planarity.h + +bin_PROGRAMS = planarity +planarity_LDADD = libplanarity.la +planarity_SOURCES = \ + c/planarityApp/planarity.c \ + c/planarityApp/planarityCommandLine.c \ + c/planarityApp/planarityHelp.c \ + c/planarityApp/planarityMenu.c \ + c/planarityApp/planarityRandomGraphs.c \ + c/planarityApp/planaritySpecificGraph.c \ + c/planarityApp/planarityTransformGraph.c \ + c/planarityApp/planarityTestAllGraphs.c \ + c/planarityApp/planarityUtils.c + +man1_MANS = c/planarityApp/planarity.1 +EXTRA_DIST=$(man1_MANS) + +TESTS = test-samples.sh + +# The @docdir@ variable will be replaced by the ./configure script. +docdir = @docdir@ +dist_doc_DATA = README.md LICENSE.TXT + +pkgconfig_DATA = libplanarity.pc diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/README.md b/extern/edge-addition-planarity-suite-Version_4.0.0.0/README.md new file mode 100644 index 000000000..d4edb3e65 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/README.md @@ -0,0 +1,73 @@ +# Edge Addition Planarity Suite + +The primary purpose of this repository is to provide implementations of the edge addition planar graph embedding algorithm and related algorithms, including a planar graph drawing method, an isolator for a minimal subgraph obstructing planarity in non-planar graphs, outerplanar graph embedder and obstruction isolator algorithms, and tester/isolator algorithms for subgraphs homeomorphic to _K2,3_, _K4_, and _K3,3_. The C implementations in this repository are the reference implementations of algorithms appearing in the following papers: + +* [Subgraph Homeomorphism via the Edge Addition Planarity Algorithm](http://dx.doi.org/10.7155/jgaa.00268) + +* [A New Method for Efficiently Generating Planar Graph Visibility Representations](http://dx.doi.org/10.1007/11618058_47) + +* [On the Cutting Edge: Simplified O(n) Planarity by Edge Addition](http://dx.doi.org/10.7155/jgaa.00091) + +* [Simplified O(n) Algorithms for Planar Graph Embedding, Kuratowski Subgraph Isolation, and Related Problems](https://dspace.library.uvic.ca/handle/1828/9918) + +A secondary purpose of this repository is to provide a generalized graph API that enables implementation of a very wide range of in-memory graph algorithms including basic methods for reading, writing, depth first search, and lowpoint as well as advanced methods for solving planarity, outerplanarity, drawing, and selected subgraph homeomorphism problems. An extension mechanism is also provided to enable implementation of planarity-related algorithms by overriding and augmenting data structures and methods of the core planarity algorithm. + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. + +### Pre-compiled Executable Releases for Non-Developers + +On several (Debian-based) distributions of Linux, you may be able to get the planarity executable with `sudo apt install planarity`, or you may already have access to its key functionality if you have SageMath or MATLAB. For non-developer users on an operating system _Platform_ supported by the release, a pre-compiled executable version of the `planarity` executable can be obtained by downloading from the release tag and then decompressing the `planarity-N.N.N.N.`_Platform_`Exe.zip` file. + +If you run the `planarity` executable program, it will offer an interactive, menu-driven mode that lets a user manually select algorithms to run and, where appropriate, files containing graphs on which to run the algorithms. + +The `planarity` executable program also supports an extensive list of command-line parameters that make it possible to automate the execution of any of the algorithms included in the application. Run `planarity` with the `-h` command-line parameter to get more information about the command line options, and use `-h -menu` for more extensive information about command-line mode. Essentially, all functionality available in the interactive, menu-driven mode is also available via the command-line parameters. + +### Setting up a Development Environment + +Please refer to the [2. Dev Setup](https://github.com/graph-algorithms/edge-addition-planarity-suite/wiki/2.-Dev-Setup) wiki page for instructions on how to install development dependencies on various supported platforms, as well as how to get started working with the project in Visual Studio Code. + +### Making the Distribution + +Once one has set up the development environment and is able to work with the code in the development environment, it is possible to make the distribution with the following additional steps: + +1. Ensure that the `autotools`, `configure`, and `make` are available on the command-line (e.g. add `C:\msys64\usr\bin` to the system `PATH` before Windows Program Files to ensure that the `find` program is the one from `MSYS2` rather than the one from Windows (e.g. adjust the `PATH` variable as needed)). +2. Open `bash` (e.g., on Windows, open the start menu and start typing "MSYS2 UCRT64" to open the correct terminal app), then within `bash` navigate to the root of the `edge-addition-planarity-suite` repository (i.e., the directory containing `configure.ac` and the `c` subdirectory) +3. Enter the following commands: + 1. `autoreconf -fi` + 2. `./configure` + 3. `make dist` + 4. `make distcheck` + +The result is a validated `planarity-N.N.N.N.tar.gz` distribution, where `N.N.N.N` is the version number expressed in the `AC_INIT` line of the `configure.ac` file. + +### Making and Running the Software from the Distribution + +If you have done the steps to set up the development environment and work with the code, then you can make and run the software using the development environment, so you don't necessarily need to make or run the software using the process below. + +You also don't necessarily need to `make` and `make install` the planarity software on Linux if you are able to get it using `sudo apt planarity` (i.e. using a Debian-based Linux distribution, which uses [`apt`](https://en.wikipedia.org/wiki/APT_(software)) for package management) + +However, you may have only downloaded the distribution (i.e., `planarity-N.N.N.N.tar.gz`) from a Release tag of this project. Once you have decompressed the distribution into a directory, you can make it by getting into `bash` (e.g., on Windows, open the start menu and start typing "MSYS2 UCRT64" to open the correct terminal app) and then entering the following commands: +1. `./configure` +2. `make` + +At this point, the `planarity` executable can be run from within the distribution directory. For example, on Windows, go to the `.libs/` subdirectory containing the `planarity` executuable and the `libplanarity` DLL and run `planarity -test ../c/samples` on the command-line. + +On Linux, the planarity program can also be installed by entering `sudo make install` on the command-line. Note that the `libplanarity` shared object and symlinks will be installed to `/usr/local/lib` so it will be necessary to set `LD_LIBRARY_PATH` accordingly. For one session, this can be done with `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib`. To make it more permanent, you could use: +1. Create a new file `/etc/ld.so.conf.d/planarity.conf` containing `/usr/local/lib` +2. Run `sudo ldconfig` + +## Versioning + +The overall project and the APIs for the graph library and the planarity-related algorithm implementations are versioned using the methods documented in [`configure.ac`](configure.ac) and [`graphLib.h`](c/graphLib/graphLib.h). The overall project version adheres to a `Major.Minor.Maintenance.Tweak` numbering system, and the `libPlanarity` shared library, which contains the graph library and planarity-related algorithm implementations, is versioned using the _current:revision:age_ system from `LibTool`. + +The `planarity.exe` application, which provides command-line and menu-driven interfaces for the graph library and planarity-related algorithms, is versioned using the overall project version defined in [`graphLib.h`](c/graphLib/graphLib.h) (see [`planarityHelp.c`](c/planarityApp/planarityHelp.c)). + +## License + +This project is licensed under a 3-clause BSD License appearing in [`LICENSE.TXT`](LICENSE.TXT). + +## Related Works and Further Documentation + +There have been successful technology transfers of the implementation code and/or algorithms of this project into other projects. To see a list of the related projects and for further documentation about this project, please see the [project wiki](https://github.com/graph-algorithms/edge-addition-planarity-suite/wiki). diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/generate_graphs.bat b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/generate_graphs.bat new file mode 100644 index 000000000..98009a716 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/generate_graphs.bat @@ -0,0 +1,20 @@ +@echo off +REM Change to correspond to the location of the geng executable +SET gengPath="C:\Users\wbkbo\git\nauty\geng.exe" + +REM Change to correspond to the root output directory +SET graphOutputDir="E:\graphs" + +SET /A N=11 +IF NOT "%1"=="" ( + SET N=%1 +) + +SET /A MAXM = (%N% * (%N% - 1^)) / 2 + +IF NOT EXIST "%graphOutputDir%\%N%" mkdir "%graphOutputDir%\%N%" + +REM Make sure to update the path to the geng executable and the directory to which you wish to write the generated graphs +FOR /L %%M IN (0,1,%MAXM%) DO ( + START /B "geng%%M" %gengPath% %N% %%M:%%M > "%graphOutputDir%\%N%\n%N%.m%%M.g6" +) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planarity_runner.bat b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planarity_runner.bat new file mode 100644 index 000000000..2a668d736 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planarity_runner.bat @@ -0,0 +1,27 @@ +@echo off + +REM Change to correspond to the location of the planarity executable +SET planarityPath="C:\Users\wbkbo\git\edge-addition-planarity-suite-fork\Release\planarity.exe" + +REM Change to correspond to the parent of the test files directory +SET graphFilesDir="E:\graphs" + +REM Change to correspond to the root output directory +SET outputDir="C:\Users\wbkbo\git\edge-addition-planarity-suite-fork\TestSupport\results" + +SET /A N=11 +SET C=p +IF NOT "%1"=="" ( + SET N=%1 + IF NOT "%2"=="" ( + SET C=%2 + ) +) + +IF NOT EXIST "%outputDir%\%N%\%C%" mkdir "%outputDir%\%N%\%C%" + +SET /A MAXM = (%N% * (%N% - 1^)) / 2 + +FOR /L %%M IN (0,1,%MAXM%) DO ( + CALL %planarityPath% -t -%C% "%graphFilesDir%\%N%\test.n%N%.m%%M.g6" "%outputDir%\%N%\%C%\n%N%.m%%M.%C%.out.txt" +) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/__init__.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/__init__.py new file mode 100644 index 000000000..504744173 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/__init__.py @@ -0,0 +1,27 @@ +"""Tools to help investigate K_{3, 3} search graph algorithm extension bug""" + +__all__ = [ + "planaritytesting_utils", + "graph_generation_orchestrator", + "planarity_testAllGraphs_orchestrator", + "g6_diff_finder", + "planarity_testAllGraphs_output_parsing", + "graph", + "edge_deletion_analysis", + "test_table_generator", +] + +import logging + +from . import planaritytesting_utils +from . import graph_generation_orchestrator +from . import planarity_testAllGraphs_orchestrator +from . import g6_diff_finder +from . import planarity_testAllGraphs_output_parsing +from . import graph +from . import edge_deletion_analysis +from . import test_table_generator + +# This ensures that no logging occurs by default unless a logger has been +# properly configured on a per-module basis +logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/edge_deletion_analysis.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/edge_deletion_analysis.py new file mode 100644 index 000000000..3074c7477 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/edge_deletion_analysis.py @@ -0,0 +1,1146 @@ +"""Automate the edge-deletion analysis on the graph(s) in a .g6 input file + +Classes: + EdgeDeletionAnalysisError + EdgeDeletionAnalyzer +""" # pylint: disable=too-many-lines + +#!/usr/bin/env python + +__all__ = ["EdgeDeletionAnalyzer", "EdgeDeletionAnalysisError"] + +import argparse +from copy import deepcopy +import logging +import os +from pathlib import Path +import re +import shutil +import subprocess +import sys +from typing import Optional + +from graph import Graph, GraphError +from planaritytesting_utils import ( + g6_header, + determine_input_filetype, + is_path_to_executable, + EDGE_DELETION_ANALYSIS_SPECIFIERS, +) + + +class EdgeDeletionAnalysisError(BaseException): + """Signals issues encountered during edge deletion analysis + + For example: + - Nonzero exit code from running various planarity commands + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class EdgeDeletionAnalyzer: + """Performs edge-deletion analysis on graph(s) in a .g6 input file""" + + # Cap this at a much lower value, since EDA for N=10 takes days if this + # threshold is high + __MAX_NUM_INVALID_OKs = 500 + + def __init__( + self, + planarity_path: Path, + infile_path: Path, + output_dir: Optional[Path] = None, + extension_choice: str = "3", + ) -> None: + """Validate input and set up for edge-deletion analysis + + If output_dir was not None and doesn't correspond to a file, + self.output_dir will be set to: + {output_dir}/{infile_path.stem}/{extension_choice} + If output_dir was None, self.output_dir will be set to + TestSupport/results/edge_deletion_analysis/{infile_path.stem}/ + {extension_choice}/ + + If the output_dir exists, all of its contents are wiped and the + directory remade. Then, the input file is copied into this directory. + + Args: + planarity_path: Path to planarity executable + infile_path: Path to graph input file + output_dir: Directory under which a new subdirectory will be + created to which results will be written. + extension_choice: Specifies the graph algorithm extension you wish + to test, either 2 for K_{2, 3} search, 3 for K_{3, 3} search + or 4 for K_4 search + + Raises: + argparse.ArgumentTypeError: If the planarity_path doesn't + correspond to an executable, if the infile_path doesn't + correspond to a file, or if the output_dir corresponds to a + file (rather than an existing directory, or to a directory that + does not yet exist) + """ + if not is_path_to_executable(planarity_path): + raise argparse.ArgumentTypeError( + f"Path for planarity executable '{planarity_path}' does not " + "correspond to an executable." + ) + + try: + file_type = determine_input_filetype(infile_path) + except ValueError as input_filetype_error: + raise argparse.ArgumentTypeError( + "Failed to determine input filetype of " f"'{infile_path}'." + ) from input_filetype_error + + if file_type != "G6": + raise argparse.ArgumentTypeError( + f"Determined '{infile_path}' has filetype '{file_type}', " + "which is not supported; please supply a .g6 file." + ) + + if extension_choice not in EDGE_DELETION_ANALYSIS_SPECIFIERS(): + raise ValueError( + f"'{extension_choice}' is not a valid graph algorithm " + "extension command specifier; expecting either 2 (K_{2, 3}), " + "3 (K_{3, 3}), or 4 (K_4)." + ) + + if not output_dir: + test_support_dir = Path(sys.argv[0]).resolve().parent.parent + output_dir = Path.joinpath( + test_support_dir, "results", "edge_deletion_analysis" + ) + + if output_dir.is_file(): + raise argparse.ArgumentTypeError( + f"Path '{output_dir}' corresponds to a file, and can't be " + "used as the output directory." + ) + + output_dir = Path.joinpath( + output_dir, f"{infile_path.stem}", f"{extension_choice}" + ) + + if Path.is_dir(output_dir): + shutil.rmtree(output_dir) + + Path.mkdir(output_dir, parents=True, exist_ok=True) + + infile_copy_path = Path.joinpath( + output_dir, infile_path.name + ).resolve() + + shutil.copyfile(infile_path, infile_copy_path) + + self.planarity_path = planarity_path.resolve() + self.orig_infile_path = infile_copy_path + self.output_dir = output_dir + self.extension_choice = extension_choice + + self._setup_logger() + + def _setup_logger(self) -> None: + """Set up logger instance for EdgeDeletionAnalyzer + Defaults to: + {self.output_dir}/ + edge_deletion_analysis-{graph_input_name}.{command}.log + """ + graph_input_name = self.orig_infile_path.stem + + log_path = Path.joinpath( + self.output_dir, + f"edge_deletion_analysis-{graph_input_name}" + f".{self.extension_choice}.log", + ) + + # logging.getLogger() returns the *same instance* of a logger + # when given the same name. In order to prevent this, must either give + # a unique name, or must prevent adding additional handlers to the + # logger + self.logger = logging.getLogger( + __name__ + f"{graph_input_name}_{self.extension_choice}" + ) + self.logger.setLevel(logging.DEBUG) + + if not self.logger.handlers: + # Create the Handler for logging data to a file + logger_handler = logging.FileHandler(filename=log_path) + logger_handler.setLevel(logging.DEBUG) + + # Create a Formatter for formatting the log messages + logger_formatter = logging.Formatter( + "%(asctime)s - [%(levelname)s] - %(module)s.%(funcName)s - " + "%(message)s" + ) + + # Add the Formatter to the Handler + logger_handler.setFormatter(logger_formatter) + + # Add the Handler to the Logger + self.logger.addHandler(logger_handler) + + stderr_handler = logging.StreamHandler() + stderr_handler.setLevel(logging.ERROR) + stderr_handler.setFormatter(logger_formatter) + self.logger.addHandler(stderr_handler) + + def analyze_graphs(self) -> int: + """Transform input graph(s) and perform edge-deletion analysis on each + + Iterates over contents of .g6 input file; for each line of the input + file, write a new .g6 file containing only that graph. Then, run the + transformation to adjacency list, and finally perform the edge-deletion + analysis steps to determine if the graph contains the target + homeomorph, either a graph homeomorphic to K_{2, 3} (extension choice + is 2), a K_{3, 3} (extension choice is 3), or a K_4 (extension choice + is 4) that was missed by the K_{2, 3} search (2), K_{3, 3} search (3) + or K_4 search (4) graph algorithm extension. + + Returns: + num_invalid_OKs: number of graphs in the infile for which + contains_missed_homeomorph was true. + + Raises: + EdgeDeletionAnalysisError: If an error occured when trying to + determine the input file type, if the input file is anything + other than a .g6 file, if transformation failed for the input + graph(s), or if any of the analysis steps failed for the input + graph(s) + """ + num_invalid_OKs = 0 + with open(self.orig_infile_path, "r", encoding="utf-8") as orig_infile: + line_num = 0 + for line in orig_infile: + line_num += 1 + if line_num == 1: + line.replace(g6_header(), "") + + new_g6_name = f"{self.orig_infile_path.name}.{line_num}" + new_parent_dir = Path.joinpath(self.output_dir, new_g6_name) + Path.mkdir(new_parent_dir, parents=True, exist_ok=True) + new_g6_path = Path.joinpath( + new_parent_dir, f"{new_g6_name}.g6" + ) + with open(new_g6_path, "w", encoding="utf-8") as new_g6_file: + new_g6_file.write(line) + + try: + adj_list_path = self.transform_input( + new_g6_path, new_parent_dir + ) + except EdgeDeletionAnalysisError as input_transform_error: + raise EdgeDeletionAnalysisError( + f"Unable to transform graph on line {line_num} in " + f"'{self.orig_infile_path}'." + ) from input_transform_error + + try: + contains_missed_homeomorph = ( + self.analyze_transformed_graph( + adj_list_path, new_parent_dir + ) + ) + except EdgeDeletionAnalysisError as analysis_error: + raise EdgeDeletionAnalysisError( + "Error encountered analyzing graph on line " + f"{line_num} in '{self.orig_infile_path}'." + ) from analysis_error + + if not contains_missed_homeomorph: + self.logger.info( + "Cleaning up '%s' directory.", new_parent_dir + ) + shutil.rmtree(new_parent_dir, ignore_errors=True) + else: + num_invalid_OKs += 1 + if ( + num_invalid_OKs + >= EdgeDeletionAnalyzer.__MAX_NUM_INVALID_OKs + ): + self.logger.error( + "Encountered more invalid OKs than supported " + "(i.e. %d)", + EdgeDeletionAnalyzer.__MAX_NUM_INVALID_OKs, + ) + break + + return num_invalid_OKs + + def transform_input(self, infile_path: Path, output_dir: Path) -> Path: + """Transforms input graph to adjacency list + + Runs + planarity -x -a {input_file} {infile_stem}.AdjList.out.txt + to transform the graph to adjacency list + + Args: + infile_path: Path to input file containing a single graph + output_dir: Directory to which transformed graph is output + + Returns: + Path to Adjacency List representation of input graph. + + Raises: + EdgeDeletionAnalysisError: if calling planarity returned nonzero + exit code + """ + adj_list_path = Path.joinpath( + output_dir, infile_path.stem + ".AdjList.out.txt" + ) + + planarity_transform_args = [ + f"{self.planarity_path}", + "-x", + "-a", + f"{infile_path}", + f"{adj_list_path}", + ] + + # TestGraphFunctionality() returns either OK or NOTOK, which means + # commandLine() will return 0 or -1. + try: + subprocess.run(planarity_transform_args, check=True) + except subprocess.CalledProcessError as e: + error_message = ( + f"Unable to transform '{infile_path}' " "to Adjacency list." + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError(error_message) from e + + return adj_list_path + + def analyze_transformed_graph( + self, adj_list_path: Path, output_dir: Path + ) -> bool: + """Perform steps of edge-deletion analysis + + First os.chdir() to the output_dir, since SpecificGraph() calls + ConstructInputFilename(), which enforces a limit on the length of the + infileName. + + Runs + planarity -s -{self.extension_to_run} \ + {infile_stem}.AdjList.out.txt \ + {infile_stem}.AdjList.s.{self.extension_to_run}.out.txt + and report whether a subgraph homeomorphic to K_{2, 3} (extension + choice is 2), K_{3, 3} (3) or K_4 (4) was found (NONEMBEDDABLE) or not + (OK). + If no subgraph homeomorphic to the target homeomorph was found (i.e. + previous test yielded OK), run + planarity -s -[po] {infile_stem}.AdjList.out.txt \ + {infile_stem}.AdjList.s.[po].out.txt \ + {infile_stem}.AdjList.s.[po].obstruction.txt + a. If the graph is reported as planar (3) or outerplanar (2, 4), then + we can be sure there is no subgraph homeomorphic to the target + homeomorph exists in the graph and execution continues without running + the rest of the loop body. + b. If the graph was reported as nonplanar (3) or nonouterplanar (2, 4), + then examine the obstruction in the secondary output file + {input_file}.AdjList.s.[po].obstruction.txt: + i. If the obstruction is homeomorphic to the target homeomorph, + then report that the original graph contains a + subgraph homeomorphic to the target homeomorph that was not found + by the respective search graph algorithm extension. + ii. If the obstruction is homeomorphic to K_4 (2), K_5 (3), or + K_{2, 3} (4), then perform the edge-deletion analysis to determine + whether the graph doesn't contain a K_{2, 3} (2), K_{3, 3} (3), or + K_4 (4) (with a high degree of confidence) + + Args: + adj_list_path: Path to adjacency list representation of input graph + output_dir: Path to output directory + + Return: + bool indicating whether or not a K_{2, 3} (2), K_{3, 3} (3), or + K_4 (4) was found by planarity (3) or by outerplanarity (2, 4) + that was not found by the respective search graph algorithm + extension. + Raises: + EdgeDeletionAnalysisError: re-raised from any step of the analysis + """ + contains_missed_homeomorph = False + orig_dir = os.getcwd() + os.chdir(output_dir) + try: + contains_target_homeomorph = self._run_homeomorph_search( + adj_list_path + ) + if not contains_target_homeomorph: + obstruction_name = self._run_alternate_check( + adj_list_path, output_dir + ) + if obstruction_name: + obstruction_type = self.determine_obstruction_type( + obstruction_name, output_dir + ) + obstruction_path = Path.joinpath( + output_dir, obstruction_name + ) + target_obstruction_type, alternate_obstruction_type = ( + self._get_forbidden_minor_pair(self.extension_choice) + ) + _, description = ( + self._get_specific_graph_alternate_check_names( + self.extension_choice + ) + ) + if obstruction_type == target_obstruction_type: + self.logger.error( + "In '%s', %sity found a %s that was not " + "found by %s search; see '%s'.", + adj_list_path, + description, + target_obstruction_type, + target_obstruction_type, + obstruction_path, + ) + contains_missed_homeomorph = True + elif obstruction_type == alternate_obstruction_type: + self.logger.info( + "'%s' contains a subgraph homeomorphic to %s; " + "proceeding with edge-deletion analysis." + "\n=======================\n", + adj_list_path, + alternate_obstruction_type, + ) + contains_missed_homeomorph = ( + self._edge_deletion_analysis( + adj_list_path, output_dir + ) + ) + self.logger.info("\n=======================\n") + if contains_missed_homeomorph: + self.logger.error( + "In '%s', edge-deletion analysis determined " + "that there is a %s that was not found by %s " + "search.", + adj_list_path, + target_obstruction_type, + target_obstruction_type, + ) + else: + self.logger.info( + "'%s' likely doesn't contain a %s.", + adj_list_path, + target_obstruction_type, + ) + except EdgeDeletionAnalysisError as e: + raise EdgeDeletionAnalysisError( + f"Encountered error when processing '{adj_list_path}'." + ) from e + else: + return contains_missed_homeomorph + finally: + os.chdir(orig_dir) + + def _run_homeomorph_search( + self, graph_infile_path: Path + ) -> Optional[bool]: + """Run homeomorph search based on extension choice + + Runs K_{2, 3} search for extension choice 2, K_{3, 3} search for + extension choice 3, or K_4 search for extension choice 4. + + Args: + graph_infile_path: Path to graph on which you wish to run the + homeomorph search algorithm + + Returns: + bool: indicates whether or not the input graph contained the target + homeomorph + + Raises: + EdgeDeletionAnalysisError: If calling SpecificGraph returned + anything other than 0 (OK) or 1 (NONEMBEDDABLE), or if messages + emitted to stdout don't align with their respective exit codes. + """ + homeomorph_search_output_name = Path( + graph_infile_path.stem + f".s.{self.extension_choice}.out.txt" + ) + + homeomorph_search_args = [ + f"{self.planarity_path}", + "-s", + f"-{self.extension_choice}", + f"{graph_infile_path.name}", + f"{homeomorph_search_output_name}", + ] + + # check=False since exit code will be nonzero if NONEMBEDDABLE, which + # is not an error state. + result = subprocess.run( + homeomorph_search_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + check=False, + ) + + target_obstruction_type, _ = self._get_forbidden_minor_pair( + self.extension_choice + ) + + _, descriptor = self._get_specific_graph_alternate_check_names( + self.extension_choice + ) + + # SpecificGraph() will return OK, NONEMBEDDABLE, or NOTOK. This means + # commandLine() will return 0, 1, or -1 for those three cases; since + # subprocess.run() with check=True would raise a CalledProcessError for + # nonzero exit codes, this logic is required to handle when we *truly* + # encounter an error, i.e. NOTOK yielding exit code -1 + if result.returncode not in (0, 1): + try: + result.check_returncode() + except ( + subprocess.CalledProcessError + ) as homeomorph_search_subprocess_error: + error_message = ( + f"Encountered an error running {target_obstruction_type} " + f"search on '{graph_infile_path}':" + f"\n\tOutput to stdout:\n\t\t{result.stdout}" + f"\n\tOutput to stderr:\n\t\t{result.stderr}" + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError( + error_message + ) from homeomorph_search_subprocess_error + + if result.returncode == 1: + if "has a subgraph homeomorphic to" not in result.stdout: + error_message = ( + f"{descriptor}ity SpecificGraph() exit code doesn't align " + f"with stdout result for '{graph_infile_path}':" + f"\n\tOutput to stdout:\n\t\t{result.stdout}" + f"\n\tOutput to stderr:\n\t\t{result.stderr}" + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError(error_message) + self.logger.info( + "'%s' contains a subgraph homeomorphic to %s; see '%s'", + graph_infile_path, + target_obstruction_type, + homeomorph_search_output_name, + ) + return True + + if result.returncode == 0: + if "has no subgraph homeomorphic to" not in result.stdout: + error_message = ( + f"{descriptor}ity SpecificGraph() exit code doesn't align " + f"with stdout result for '{graph_infile_path}':" + f"\n\tOutput to stdout:\n\t\t{result.stdout}" + f"\n\tOutput to stderr:\n\t\t{result.stderr}" + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError(error_message) + self.logger.info( + "'%s' is reported as not containing a " + "subgraph homeomorphic to %s.", + graph_infile_path, + target_obstruction_type, + ) + return False + + return False + + def _run_alternate_check( + self, graph_infile_path: Path, output_dir: Path + ) -> Optional[Path]: + """Invoke planarity (extension choice is 3) or outerplanarity (4) + + Args: + graph_infile_path: Path to graph on which you wish to run + SpecificGraph for planarity (3) or outerplanarity (4) + output_dir: Path to which you wish to output results + + Returns: + obstruction_name: Path object that only contains the stem and + extension of the obstruction; expected to be a file within + output_dir. + + Raises: + EdgeDeletionAnalysisError: if calling planarity/outerplanarity + returned anything other than 0 (OK) or 1 (NONEMBEDDABLE), or + if messages emitted to stdout don't align with their respective + exit codes + """ + command_specifier, descriptor = ( + self._get_specific_graph_alternate_check_names( + self.extension_choice + ) + ) + + output_name = Path( + graph_infile_path.stem + f".s.{command_specifier}.out.txt" + ) + obstruction_name = Path( + graph_infile_path.stem + f".s.{command_specifier}.obstruction.txt" + ) + + core_planarity_args = [ + f"{self.planarity_path}", + "-s", + f"-{command_specifier}", + f"{graph_infile_path.name}", + f"{output_name}", + f"{obstruction_name}", + ] + + # check=False since we want to handle nonzero exit code appropriately + result = subprocess.run( + core_planarity_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + check=False, + ) + + # SpecificGraph() will return OK, NONEMBEDDABLE, or NOTOK. This means + # commandLine() will return 0, 1, or -1 for those three cases; since + # subprocess.run() with check=True would raise a CalledProcessError for + # nonzero exit codes, this logic is required to handle when we *truly* + # encounter an error, i.e. NOTOK yielding exit code -1 + if result.returncode not in (0, 1): + try: + result.check_returncode() + except subprocess.CalledProcessError as e: + error_message = ( + "Encountered an error running " + f"{descriptor}rity on '{graph_infile_path}':" + f"\n\tOutput to stdout:\n\t\t{result.stdout}" + f"\n\tOutput to stderr:\n\t\t{result.stderr}" + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError(error_message) from e + + if result.returncode == 1: + if f"is not {descriptor}" not in result.stdout: + error_message = ( + f"{descriptor}ity SpecificGraph() exit code doesn't align " + f"with stdout result for '{graph_infile_path}':" + f"\n\tOutput to stdout:\n\t\t{result.stdout}" + f"\n\tOutput to stderr:\n\t\t{result.stderr}" + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError(error_message) + self.logger.info( + "'%s' is non%s; see '%s'", + graph_infile_path, + descriptor, + obstruction_name, + ) + elif result.returncode == 0: + if f"is {descriptor}" not in result.stdout: + error_message = ( + f"{descriptor}ity SpecificGraph() exit code doesn't align " + f"with stdout result for '{graph_infile_path}':" + f"\n\tOutput to stdout:\n\t\t{result.stdout}" + f"\n\tOutput to stderr:\n\t\t{result.stderr}" + ) + self.logger.error(error_message) + raise EdgeDeletionAnalysisError(error_message) + output_path = Path.joinpath(output_dir, output_name) + self.logger.info( + "'%s' is reported as %s, as shown in '%s'.", + graph_infile_path, + descriptor, + output_path, + ) + obstruction_name = None + + return obstruction_name + + def determine_obstruction_type( + self, obstruction_name: Path, output_dir: Path + ) -> str: + """Determine obstruction type based on vertex degree counts + + Args: + obstruction_name: name of secondary output file produced by + SpecificGraph invoking planarity (extension choice is 3) or + outerplanarity (extension choice is 4) + output_dir: Directory containing obstruction_name + + Returns: + - 'K_{2, 3}' if there are two vertices of degree 3, and all other + vertices have degree 2 or 0 + - 'K_{3, 3}' if there are six vertices of degree 3, and all other + vertices have degree 2 or 0 + - 'K_4' if there are four vertices of degree 3, and all other + vertices have degree 2 or 0 + - 'K_5' if there are 5 vertices of degree 4, and all other vertices + have degree 2 or 0 + + Raises: + EdgeDeletionAnalysisError: If error encountered when trying to read + obstruction graph from file, or if the obstruction found + doesn't have the expected characteristics of any of K_5, + K_{3, 3}, K_{2, 3}, or K_4 + """ + try: + obstruction_graph = EdgeDeletionAnalyzer._read_adj_list_graph_repr( + obstruction_name + ) + except EdgeDeletionAnalysisError as read_adj_list_error: + raise EdgeDeletionAnalysisError( + "Encountered error when trying to read obstruction subgraph " + "from " + f"'{Path.joinpath(output_dir, obstruction_name)}'" + ) from read_adj_list_error + + vertex_degree_counts = obstruction_graph.get_vertex_degree_counts() + vertices_with_nonzero_degree = ( + obstruction_graph.order - vertex_degree_counts.get(0, 0) + ) + num_vertices_of_deg_2 = vertex_degree_counts.get(2, 0) + num_vertices_of_deg_3 = vertex_degree_counts.get(3, 0) + num_vertices_of_deg_4 = vertex_degree_counts.get(4, 0) + + if ( + num_vertices_of_deg_3 == 2 + and num_vertices_of_deg_2 == (vertices_with_nonzero_degree - 2) + and all( + count == 0 + for deg, count in vertex_degree_counts.items() + if deg != 0 + if deg != 2 + if deg != 3 + ) + ): + return "K_{2, 3}" + + if ( + num_vertices_of_deg_3 == 6 + and num_vertices_of_deg_2 == (vertices_with_nonzero_degree - 6) + and all( + count == 0 + for deg, count in vertex_degree_counts.items() + if deg != 0 + if deg != 2 + if deg != 3 + ) + ): + return "K_{3, 3}" + + if ( + num_vertices_of_deg_3 == 4 + and num_vertices_of_deg_2 == (vertices_with_nonzero_degree - 4) + and all( + count == 0 + for deg, count in vertex_degree_counts.items() + if deg != 0 + if deg != 2 + if deg != 3 + ) + ): + return "K_4" + + if ( + num_vertices_of_deg_4 == 5 + and num_vertices_of_deg_2 == (vertices_with_nonzero_degree - 5) + and all( + count == 0 + for deg, count in vertex_degree_counts.items() + if deg != 0 + if deg != 2 + if deg != 4 + ) + ): + return "K_5" + + raise EdgeDeletionAnalysisError( + "The obstruction found doesn't have the expected " + "characteristics ofof any of K_5, K_{3, 3}, K_{2, 3}, or K_4; " + f"see '{Path.joinpath(output_dir, obstruction_name)}'." + ) + + @staticmethod + def _get_forbidden_minor_pair( + extension_choice: str, + ) -> tuple[str, str]: + """Gets pair of "forbidden minors" for given extension choice + + Args: + extension_choice: either 2 (K_{2, 3} search), 3 (K_{3, 3} search), + or 4 (K_4 search) + + Returns: + The pair of forbidden minors for the given extension choice + """ + if extension_choice not in EDGE_DELETION_ANALYSIS_SPECIFIERS(): + raise EdgeDeletionAnalysisError( + f"Invalid extension choice: '{extension_choice}'" + ) + + target_obstruction_correspondence = { + "2": "K_{2, 3}", + "3": "K_{3, 3}", + "4": "K_4", + } + + alternate_obstruction_correspondence = { + "2": "K_4", + "3": "K_5", + "4": "K_{2, 3}", + } + + target_obstruction_type = target_obstruction_correspondence[ + extension_choice + ] + alternate_obstruction_type = alternate_obstruction_correspondence[ + extension_choice + ] + + return target_obstruction_type, alternate_obstruction_type + + @staticmethod + def _get_specific_graph_alternate_check_names( + extension_choice: str, + ) -> tuple[str, str]: + """ + Args: + extension_choice: either 2 (K_{2, 3} search), 3 (K_{3, 3} search), + or 4 (K_4 search) + + Returns: + The pair of command_specifier and descriptor for the given + extension choice + """ + if extension_choice not in EDGE_DELETION_ANALYSIS_SPECIFIERS(): + raise EdgeDeletionAnalysisError( + f"Invalid extension choice: '{extension_choice}'" + ) + + command_correspondence = {"2": "o", "3": "p", "4": "o"} + + descriptor_correspondence = { + "2": "outerplanar", + "3": "planar", + "4": "outerplanar", + } + + command_specifier = command_correspondence[extension_choice] + descriptor = descriptor_correspondence[extension_choice] + + return command_specifier, descriptor + + @staticmethod + def _read_adj_list_graph_repr(graph_path: Path) -> Graph: + """Static method to read graph as adjacency list from file + + Args: + graph_path: Path to 0-based adjacency-list representation of graph + we wish to use to initialize a Graph object + + Returns: + Graph with order and graph_adj_list_repr corresponding to file + contents. + + Raises: + EdgeDeletionAnalysisError: If file header is invalid, or if + GraphError was encountered when we tried to add an edge based + on file contents. + """ + with open(graph_path, "r", encoding="utf-8") as graph_file: + header = graph_file.readline() + match = re.match(r"N=(?P\d+)", header) + if not match: + raise EdgeDeletionAnalysisError( + f"Infile '{graph_path}' doesn't contain " + "an adjacency list: invalid header." + ) + + order = int(match.group("order")) + graph = Graph(order) + line_num = 0 + for line in graph_file: + line_num += 1 + line_elements = line.split(":") + u = int(line_elements[0]) + neighbours = [ + int(v) for v in line_elements[1].split() if v if v != "-1" + ] + for v in neighbours: + try: + graph.add_arc(u, v) + except GraphError as e: + raise EdgeDeletionAnalysisError( + f"Unable to add edge ({u}, {v}) as specified on" + f"line {line_num} of '{graph_path}'." + ) from e + + return graph + + def _edge_deletion_analysis( # pylint: disable=too-many-locals + self, adj_list_path: Path, output_dir: Path + ) -> bool: + """Search for homeomorph by removing edges and running alternate check + + If the original graph was determined to not contain a subgraph + homeomorphic to K_{2, 3} (extension choice is 2), K_{3, 3} (3), or + K_4 (4), but was reported as nonplanar (3) or nonouterplanar (2, 4) and + the obstruction found was homeomorphic to K_4 (2), K_5 (3) or + K_{2, 3} (4), we want to see if there is no subgraph homeomorphic to + K_{2, 3} (2), K_{3, 3} (3) or K_4 (4) with a high degree of confidence + (not a guarantee). + + This is done by iterating over the graph_adj_list_repr of the original + graph: + - for each edge, if the head vertex label is greater than the + tail vertex label, create a deepcopy of the original graph and + remove that edge before running planarity/outerplanarity + - if the graph-minus-one-edge is planar/outerplanar, we continue to + the next loop iteration to see if removing a different edge will + reveal the target obstruction + - if the graph-minus-one-edge is nonplanar/nonouterplanar, we check + the obstruction type: + - if the obstruction is homeomorphic to the target homeomorph, we + emit a message to indicate that we found a target homeomorph + that was missed by their respective search + - if the obstruction is homeomorphic to the K_4 (2), K_5 (3), or + K_{2, 3} (4), run K_{2, 3} search (2), K_{3, 3} search (3), or + K_4 search (4) on the graph-minus-one-edge; either: + - we report that a subgraph homeomorphic to the target graph + was missed by the original call to the graph search extension + - we report that no subgraph homeomorphic to the target graph + was found by the edge-deletion-analysis. + It may be the case that the graph-minus-one-edge still has the same + underlying structure that causes the issue with K_{2, 3} search (2), + K_{3, 3} search (3), or K_4 search (4) on the original graph, so even + this additional test is not a 100% guarantee of finding the target + homeomorph that we should have found in the original graph. + + Args: + adj_list_path: Path to adjacency list upon which to perform + edge-deletion analysis + output_dir: Directory to which you wish to output the results of + edge-deletion analysis on the input graph + + Returns: + bool indicating at least one K_{2, 3}/K_{3, 3}/K_4 was found as a + result of the edge-deletion manipulation. + + Raises: + EdgeDeletionAnalysisError is raised if: + - the original adjacency list produced by the transform is + 1-based rather than the expected 0-based + - this could happen if rather than starting with a graph in + .g6 format, your input graph is an adjacency list produced + by planarity when NIL is set to 0 rather than -1, causing + gp_GetFirstVertex(theGraph) to return 1 and line + terminators to be set to 0 because + theGraph->internalFlags & FLAGS_ZEROBASEDIO is falsy + - Deleting an edge from the copied graph fails + - Running planarity on the original-graph-minus-one-edge fails + - Determining the type of obstruction found fails + """ + try: + orig_graph = EdgeDeletionAnalyzer._read_adj_list_graph_repr( + adj_list_path + ) + except EdgeDeletionAnalysisError as e: + raise EdgeDeletionAnalysisError( + "Unable to read original graph adjacency list representation" + f"from file '{adj_list_path}'." + ) from e + + adj_list_stem = ".".join(adj_list_path.name.split(".")[:-3]) + adj_list_suffix = "".join(adj_list_path.suffixes[-3:]) + + orig_graph_contains_homeomorph = False + u = -1 + for adj_list in orig_graph.graph_adj_list_repr: + u += 1 + adj_list_index = -1 + for v in adj_list: + adj_list_index += 1 + if v < u: + continue + + orig_graph_minus_edge = deepcopy(orig_graph) + try: + orig_graph_minus_edge.delete_edge(u, v) + except EdgeDeletionAnalysisError as delete_edge_error: + raise EdgeDeletionAnalysisError( + f"Unable to delete edge {{{u}, {v}}} from copied graph" + ) from delete_edge_error + + orig_graph_minus_edge_path = Path.joinpath( + adj_list_path.parent, + f"{adj_list_stem}.rem{u}-{v}{adj_list_suffix}", + ) + with open( + orig_graph_minus_edge_path, "w", encoding="utf-8" + ) as outfile: + outfile.write(str(orig_graph_minus_edge)) + + try: + obstruction_name = self._run_alternate_check( + orig_graph_minus_edge_path, output_dir + ) + except EdgeDeletionAnalysisError as e: + raise EdgeDeletionAnalysisError( + "Failed to run planarity on " + f"'{orig_graph_minus_edge_path}'." + ) from e + + if not obstruction_name: + continue + + try: + obstruction_type = self.determine_obstruction_type( + obstruction_name, output_dir + ) + except EdgeDeletionAnalysisError as e: + raise EdgeDeletionAnalysisError( + "Failed to determine obstruction type of graph " + f"produced by removing edge {{{u}, {v}}} from the " + "original graph." + ) from e + planar_obstruction_path = Path.joinpath( + output_dir, obstruction_name + ) + target_obstruction_type, alternate_obstruction_type = ( + self._get_forbidden_minor_pair(self.extension_choice) + ) + + if obstruction_type == target_obstruction_type: + self.logger.info( + "'%s' contains a %s that was not found by %s search; " + "see '%s'.", + orig_graph_minus_edge_path, + target_obstruction_type, + target_obstruction_type, + planar_obstruction_path, + ) + orig_graph_contains_homeomorph = True + elif obstruction_type == alternate_obstruction_type: + self.logger.info( + "'%s' contains a subgraph homeomorphic to %s; see " + "'%s'. Attempting to see if edge-removal allows %s " + "search to find the target homeomorph.", + orig_graph_minus_edge_path, + alternate_obstruction_type, + planar_obstruction_path, + target_obstruction_type, + ) + orig_graph_minus_edge_contains_homeomorph = ( + self._run_homeomorph_search(orig_graph_minus_edge_path) + ) + if orig_graph_minus_edge_contains_homeomorph: + self.logger.info( + "'%s' contains a %s that was not found by %s " + "search; see '%s'.", + orig_graph_minus_edge_path, + target_obstruction_type, + target_obstruction_type, + planar_obstruction_path, + ) + orig_graph_contains_homeomorph = True + else: + self.logger.info( + "'%s' doesn't appear to contain a %s.", + orig_graph_minus_edge_path, + target_obstruction_type, + ) + + return orig_graph_contains_homeomorph + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="Edge deletion analysis tool\n\n" + "When given an input file, determine the type of input. If " + "the file is a not a .g6 file, an error is raised. Otherwise, " + "iterate over each line and create a separate .g6 file:\n" + "\t{input_file.stem}.{line_num}.g6\n" + "And perform the following steps on each graph:\n" + "1. Runs\n" + "\tplanarity -x -a {input_file} {infile_stem}.AdjList.out" + ".txt\n" + "to transform the graph to adjacency list\n" + "2. Runs\n" + "\tplanarity -s -{extension_choice} {infile_stem}.AdjList.out.txt " + "{infile_stem}.AdjList.s.{extension_choice}.out.txt\n" + "and report whether a subgraph homeomorphic to K_{2, 3} (extension " + "choice is 2), K_{3, 3} (extension choice is 3), or K_4 (extension " + "choice is 4) was found (NONEMBEDDABLE) or not (OK).\n" + "3. If no subgraph homeomorphic to the target homeomorph was found " + "(i.e. previous test yielded OK), run\n" + "\tplanarity -s -p {infile_stem}.AdjList.out.txt " + "{infile_stem}.AdjList.s.[po].out.txt {infile_stem}.AdjList.s.[po]." + "obstruction.txt\n" + "Where we run planarity if the extension choice is 3 or " + "outerplanarity if the extension choice is 2 or 4.\n" + "\ta. If the graph is reported as planar/outerplanar, then we can be " + "sure the target homeomorph will not be present in the graph and " + "execution terminates.\n" + "\tb. If the graph was reported as nonplanar/nonouterplanar, examine " + "the obstruction in \n" + "\t\t{input_file}.AdjList.s.[po].obstruction.txt:\n" + "\t\ti. If the obstruction is homeomorphic to K_{2, 3}/K_{3, 3}/K_4, " + "then report that the original graph contains the target homeomorph " + "that was not found by the respective graph search extension.\n" + "\t\tii. If the obstruction is homeomorphic to the other forbidden " + "minor (K_4 for K{2, 3}, K_5 for K_{3, 3}, or K_{2, 3} for K_4) then " + "perform the edge-deletion analysis to determine if the original " + "graph contains a subgraph homeomorphic to the target homeomorph that" + "wasn't found by the corresponding graph search extension, or if the " + "original graph doesn't contain the target homeomorph (with high " + "confidence).", + ) + parser.add_argument( + "-p", + "--planaritypath", + type=Path, + required=True, + help="Path to planarity executable", + metavar="PATH_TO_PLANARITY_EXECUTABLE", + ) + parser.add_argument( + "-i", + "--inputfile", + type=Path, + required=True, + help="Path to graph(s) to analyze", + metavar="PATH_TO_GRAPH(s)", + ) + parser.add_argument( + "-o", + "--outputdir", + type=Path, + default=None, + metavar="OUTPUT_DIR_PATH", + help="Parent directory under which subdirectory named after " + "{infile_stem} will be created for output results. If none " + "provided, defaults to:\n" + "\tTestSupport/results/edge_deletion_analysis/{infile_stem}", + ) + parser.add_argument( + "-c", + "--command", + type=str, + metavar="ALGORITHM_COMMAND", + default="3", + help="Graph algorithm command specifier, either 2 (K_{2, 3} search), " + "3 (K_{3, 3} search), or 4 (K_4 search)", + ) + + args = parser.parse_args() + + eda = EdgeDeletionAnalyzer( + planarity_path=args.planaritypath, + infile_path=args.inputfile, + output_dir=args.outputdir, + extension_choice=args.command, + ) + + num_invalidOKs = eda.analyze_graphs() + eda.logger.info("NUMBER OF INVALID OKs: %d", num_invalidOKs) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_diff_finder.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_diff_finder.py new file mode 100644 index 000000000..319ae24f8 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_diff_finder.py @@ -0,0 +1,617 @@ +"""Tools to analyze .g6 files produced by nauty geng + +Classes: + G6DiffFinderException + G6DiffFinder +""" + +#!/usr/bin/env python + +__all__ = ["G6DiffFinder", "G6DiffFinderException"] + +import os +import sys +import json +import argparse +import logging +from pathlib import Path +from typing import Optional + +from planaritytesting_utils import g6_header, determine_input_filetype + + +class G6DiffFinderException(Exception): + """ + Custom exception for representing errors that arise when processing two + .g6 files. + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class G6DiffFinder: + """Tool to identify the diffs between two .g6 input files.""" + + def __init__( + self, + first_comparand_infile_path: Path, + second_comparand_infile_path: Path, + log_path: Optional[Path] = None, + ) -> None: + """ + Initializes G6DiffFinder instance. + + For each .g6 path provided, validates the infile path and then + populates the corresponding comparand dict. + + Args: + first_comparand_infile_path: path to a .g6 file + second_comparand_infile_path: path to a .g6 file + + Raises: + ValueError: If either first_comparand_infile_path or + second_comparand_infile_path are invalid + FileNotFoundError: If _populate_comparand_dict() failed to open + either input file, re-raise error + """ + self._setup_logger(log_path) + + try: + first_comparand_filetype = determine_input_filetype( + first_comparand_infile_path + ) + except ValueError as comparand_infile_error: + raise comparand_infile_error + + if first_comparand_filetype != "G6": + raise ValueError( + f"'{first_comparand_infile_path}' is not a .g6 file." + ) + + try: + self._first_comparand_dict = self._populate_comparand_dict( + first_comparand_infile_path + ) + except FileNotFoundError as comparand_infile_not_found_error: + raise FileNotFoundError( + "Unable to populate comparand dict: can't open first input file" + ) from comparand_infile_not_found_error + + try: + second_comparand_filetype = determine_input_filetype( + second_comparand_infile_path + ) + except ValueError as comparand_infile_error: + raise comparand_infile_error + + if second_comparand_filetype != "G6": + raise ValueError( + f"'{second_comparand_infile_path}' is not a .g6 file." + ) + + try: + self._second_comparand_dict = self._populate_comparand_dict( + second_comparand_infile_path + ) + except FileNotFoundError as comparand_infile_not_found_error: + raise FileNotFoundError( + "Unable to populate comparand dict: " + "can't open second input file" + ) from comparand_infile_not_found_error + + def _setup_logger(self, log_path: Optional[Path] = None) -> None: + if not log_path: + test_support_dir = Path(sys.argv[0]).resolve().parent.parent + g6_diff_finder_logdir = Path.joinpath( + test_support_dir, "g6_diff_finder_logs" + ) + Path.mkdir(g6_diff_finder_logdir, parents=True, exist_ok=True) + log_path = Path.joinpath(g6_diff_finder_logdir, "G6DiffFinder.log") + if log_path.is_file(): + os.remove(log_path) + + # logging.getLogger() returns the *same instance* of a logger + # when given the same name. In order to prevent this, must either give + # a unique name, or must prevent adding additional handlers to the + # logger + self.logger = logging.getLogger(__name__ + str(log_path.name)) + self.logger.setLevel(logging.DEBUG) + + if not self.logger.handlers: + # Create the Handler for logging data to a file + logger_handler = logging.FileHandler(filename=log_path) + logger_handler.setLevel(logging.DEBUG) + + # Create a Formatter for formatting the log messages + logger_formatter = logging.Formatter( + "%(asctime)s - [%(levelname)s] - %(module)s.%(funcName)s - " + "%(message)s" + ) + + # Add the Formatter to the Handler + logger_handler.setFormatter(logger_formatter) + + # Add the Handler to the Logger + self.logger.addHandler(logger_handler) + + def _populate_comparand_dict( + self, comparand_infile_path: Path + ) -> dict[str, Path | dict[str, int | list[int]]]: + """ + Opens the file corresponding to path comparand_infile_path, then + iterates over the lines of the file object. If the first line contains + the .g6 header, it is stripped from the line contents. Then, + the line contents are stripped of whitespace, after which we check to + see if the graph encoding already has appeared in the file. If so, we + add the current line_num to the duplicate_line_nums list corresponding + to that encoding. If not, we insert a key-value pair into the comparand + dict corresponding to the graph encoding mapped to a sub-dict with the + first_line on which the encoding occurs and an empty list of + duplicate_line_nums in case that same encoding appears again in the + file. + + Args: + comparand_infile_path: path to a .g6 file + Raises: + FileNotFoundError: If open() fails on comparand_infile_path + """ + try: + self.logger.info( + "Populating comparand dict from infile path '%s'.", + comparand_infile_path, + ) + + with open( + comparand_infile_path, "r", encoding="utf-8" + ) as comparand_infile: + comparand_dict = {} + line_num = 1 + for line in comparand_infile: + if line_num == 1: + line = line.replace(g6_header(), "") + line = line.strip() + if not line: + continue + if line in comparand_dict: + if not comparand_dict[line].get("duplicate_line_nums"): + comparand_dict[line]["duplicate_line_nums"] = [] + comparand_dict[line]["duplicate_line_nums"].append( + line_num + ) + else: + comparand_dict[line] = { + "first_line": line_num, + "duplicate_line_nums": [], + } + line_num += 1 + comparand_dict["infile_path"] = comparand_infile_path + return comparand_dict + except FileNotFoundError as comparand_file_not_found_error: + raise FileNotFoundError( + "Unable to open comparand infile with path " + f"'{comparand_infile_path}'." + ) from comparand_file_not_found_error + + def output_duplicates(self): + """Output duplicates in each comparand dict + + Calls self._output_duplicates() for each comparand dict so that we have + two separate output files containing the duplicates within their + respective .g6 input files. + + Raises: + G6DiffFinderException: Re-raises if _output_duplicates() failed on + either comparand dict + """ + try: + self._output_duplicates(self._first_comparand_dict) + except G6DiffFinderException as first_comparand_find_duplicates_error: + raise G6DiffFinderException( + "Unable to output duplicates for first .g6 file." + ) from first_comparand_find_duplicates_error + + try: + self._output_duplicates(self._second_comparand_dict) + except G6DiffFinderException as second_comparand_find_duplicates_error: + raise G6DiffFinderException( + "Unable to output duplicates for second .g6 file." + ) from second_comparand_find_duplicates_error + + def _output_duplicates(self, comparand_dict: dict): + """ + Performs a dictionary-comprehension to get each g6_encoded_graph whose + corresponding value has a non-empty duplicate_line_nums list. + + If this dictionary is empty, emits a log message that no duplicates + were found. + + If the dictionary is nonempty, json.dumps() to an output file whose + path is: + {comparand_infile_path}.duplicates.out.txt + + The file is overwritten if it already exists. + + Args: + comparand_dict: contains key-value pairs of graph encodings mapped + to sub-dicts, which contain the first_line on which the + encoding occurred and the list of duplicate_line_nums on which + the graph recurred in the file. + + Raises: + G6DiffFinderException: If KeyError encountered when trying to + get value corresponding to key infile_path from the comparand + dict + """ + try: + comparand_infile_path = Path( + comparand_dict["infile_path"] + ).resolve() + except KeyError as no_infile_path_error: + raise G6DiffFinderException( + "Invalid dict structure: missing 'infile_path'." + ) from no_infile_path_error + else: + comparand_outfile_results_directory_path = Path.joinpath( + comparand_infile_path.parent, "results" + ) + Path.mkdir( + comparand_outfile_results_directory_path, + parents=True, + exist_ok=True, + ) + comparand_outfile_path = Path.joinpath( + comparand_outfile_results_directory_path, + comparand_infile_path.parts[-1] + ".duplicates.out.txt", + ) + + duplicated_g6_encodings = { + g6_encoded_graph: comparand_dict[g6_encoded_graph] + for g6_encoded_graph in comparand_dict + if g6_encoded_graph != "infile_path" + if len(comparand_dict[g6_encoded_graph]["duplicate_line_nums"]) + > 0 + } + + if not duplicated_g6_encodings: + self.logger.info( + "No duplicates present in '%s'.", comparand_infile_path + ) + else: + with open( + comparand_outfile_path, "w", encoding="utf-8" + ) as comparand_outfile: + self.logger.info( + "Outputting duplicates present in '%s'", + comparand_infile_path, + ) + comparand_outfile.write( + "Comparand infile name: " + f"'{comparand_infile_path.name}'\n" + ) + comparand_outfile.write( + json.dumps(duplicated_g6_encodings, indent=4) + ) + + def graph_set_diff(self): + """ + Calls self._graph_set_diff() for both the self._first_comparand_dict + against the second self._second_comparand_dict and vice versa to output + which graphs are in the first .g6 file that are absent from the second, + and in the second .g6 file that are absent from the first. + """ + self._graph_set_diff( + self._first_comparand_dict, self._second_comparand_dict + ) + self._graph_set_diff( + self._second_comparand_dict, self._first_comparand_dict + ) + + def _graph_set_diff( + self, first_comparand_dict: dict, second_comparand_dict: dict + ): + """ + Gets the first_comparand_infile_dir (which is where the output + file will be written to, if the result is nonempty) and the infile + names that were used to populate the respective dicts; these are used + to construct the outfile path. + + Performs a list-comprehension to determine which graph encodings appear + in the first file that don't appear in the second. + + If the graphs_in_first_but_not_second is empty, emits a log message + indicating that no graphs were found in the first dict that were not + in the second. + + If graphs_in_first_but_not_second is nonempty, writes each graph + encoding followed by a newline so that the output constitutes a valid + .g6 file. + + The output filepath will be of the form + {first_comparand_infile_dir}/results/ + graphs_in_{first_comparand_infile_name}_not_in_{second_comparand_infile_name}.g6 + + Args: + first_comparand_dict: contains key-value pairs of graph encodings + mapped to sub-dicts, which contain the first_line on which the + encoding occurred and the list of duplicate_line_nums on which + the graph recurred in the file. + second_comparand_dict: Same structure as first_comparand_dict. + + Raises: + G6DiffFinderException: Re-raises if exeption encountered when we + _get_infile_names() from the two comparand dicts + """ + try: + ( + first_comparand_infile_dir, + first_comparand_infile_name, + second_comparand_infile_name, + ) = self._get_infile_names( + first_comparand_dict, second_comparand_dict + ) + except G6DiffFinderException as get_infile_names_error: + raise G6DiffFinderException( + "Unable to extract infile_names from comparand dicts." + ) from get_infile_names_error + + graphs_in_first_but_not_second = [ + g6_encoding + for g6_encoding in first_comparand_dict + if g6_encoding != "infile_path" + if g6_encoding not in second_comparand_dict + ] + + if not graphs_in_first_but_not_second: + self.logger.info( + "No graphs present in '%s' that aren't in '%s'", + first_comparand_infile_name, + second_comparand_infile_name, + ) + else: + comparand_outfile_results_directory_path = Path.joinpath( + first_comparand_infile_dir, "results" + ) + Path.mkdir( + comparand_outfile_results_directory_path, + parents=True, + exist_ok=True, + ) + outfile_path = Path.joinpath( + comparand_outfile_results_directory_path, + f"graphs_in_{first_comparand_infile_name}_not_in_" + + f"{second_comparand_infile_name}.g6", + ) + self.logger.info( + "Outputting graphs present in '%s' that aren't in '%s' to " + "'%s'.", + first_comparand_infile_name, + second_comparand_infile_name, + outfile_path, + ) + with open( + outfile_path, "w", encoding="utf-8" + ) as graph_set_diff_outfile: + for g6_encoding in graphs_in_first_but_not_second: + graph_set_diff_outfile.write(g6_encoding) + graph_set_diff_outfile.write("\n") + + def graph_set_intersection_with_different_line_nums(self): + """ + Takes the two comparand dicts associated with the G6DiffFinder and gets + the first_comparand_infile_dir (which is where the output + file will be written to, if the result is nonempty) and the infile + names corresponding to the self._first_comparand_dict and + self._second_comparand_dict; these are used to construct the outfile + path. + + Performs a dictionary comprehension to produce key-value pairs of + graph encoding mapped to a tuple containing the first_line on which the + encoding occurred in the first .g6 infile and the first_line on which + the encoding occurred in the second .g6 infile. + + If the graphs_in_first_and_second is empty, emits a log message + indicating that the intersection is empty. + + If graphs_in_first_and_second is nonempty, json.dumps() the dict to the + output file of the form: + {first_comparand_infile_dir}/results/graphs_in_{first_comparand_infile_name}_and_{second_comparand_infile_name}.txt # pylint: disable=line-too-long + + Raises: + G6DiffFinderException: Re-raises if exeption encountered when we + _get_infile_names() from the two comparand dicts + """ + try: + ( + first_comparand_infile_dir, + first_comparand_infile_name, + second_comparand_infile_name, + ) = self._get_infile_names( + self._first_comparand_dict, self._second_comparand_dict + ) + except G6DiffFinderException as get_infile_names_error: + raise G6DiffFinderException( + "Unable to extract infile_names from comparand dicts." + ) from get_infile_names_error + + graphs_in_first_and_second = { + g6_encoding: ( + self._first_comparand_dict[g6_encoding]["first_line"], # type: ignore + self._second_comparand_dict[g6_encoding]["first_line"], # type: ignore + ) + for g6_encoding in self._first_comparand_dict + if g6_encoding != "infile_path" + if g6_encoding in self._second_comparand_dict + if ( + self._first_comparand_dict[g6_encoding]["first_line"] # type: ignore + != self._second_comparand_dict[g6_encoding][ # type: ignore + "first_line" + ] # type: ignore + ) + } + + if not graphs_in_first_and_second: + self.logger.info( + "No graphs present in both '%s' and '%s' that appear on " + "different lines.", + first_comparand_infile_name, + second_comparand_infile_name, + ) + else: + comparand_outfile_results_directory_path = Path.joinpath( + first_comparand_infile_dir, "results" + ) + Path.mkdir( + comparand_outfile_results_directory_path, + parents=True, + exist_ok=True, + ) + outfile_path = Path.joinpath( + comparand_outfile_results_directory_path, + f"graphs_in_{first_comparand_infile_name}_and_" + + f"{second_comparand_infile_name}.txt", + ) + self.logger.info( + "Outputting graphs present in both '%s' and '%s' that " + "appear on different lines to '%s'.", + first_comparand_infile_name, + second_comparand_infile_name, + outfile_path, + ) + with open( + outfile_path, "w", encoding="utf-8" + ) as graph_set_intersection_outfile: + graph_set_intersection_outfile.write( + json.dumps(graphs_in_first_and_second, indent=4) + ) + + def _get_infile_names( + self, first_comparand_dict: dict, second_comparand_dict: dict + ): + """ + Uses pathlib.Path object's .parent attribute to get the directory of + the first .g6 input file, stored in the first_comparand_dict's + infile_path attribute. Then, gets the first_comparand_infile_name by + stripping the .g6 extension, and likewise for the + second_comparand_infile_name. + + Args: + first_comparand_dict: Dict containing the key infile_name, + in addition to the key-value pairs of graph encodings mapped to + their first_line and the list of duplicate_line_nums + second_comparand_dict: Same structure as first_comparand_dict + + Returns: + first_comparand_infile_dir: Parent directory of the first .g6 file + first_comparand_infile_name: base name of the first .g6 file + second_comparand_infile_name: base name of the second .g6 file + + Raises: + G6DiffFinderException: If KeyError encountered when trying to + get value corresponding to key infile_path from either of the + two comparand dicts + """ + try: + first_comparand_infile_path = Path( + first_comparand_dict["infile_path"] + ).resolve() + except KeyError as key_error: + raise G6DiffFinderException( + "Invalid dict structure: missing key 'infile_path'." + ) from key_error + + first_comparand_infile_dir = first_comparand_infile_path.parent + first_comparand_infile_name = first_comparand_infile_path.with_suffix( + "" + ).name + try: + second_comparand_infile_name = ( + Path(second_comparand_dict["infile_path"]).with_suffix("").name + ) + except KeyError as key_error: + raise G6DiffFinderException( + "Invalid dict structure: missing key 'infile_path'." + ) from key_error + + return ( + first_comparand_infile_dir, + first_comparand_infile_name, + second_comparand_infile_name, + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="Tool to help interrogate and compare two .g6 files.\n" + "- Determines if there are duplicates in the first and second " + "comparand .g6 files; outputs to files with paths:\n" + "\t{first_comparand_infile_dir}/results/" + "{first_comparand_infile_name}.duplicates.out.txt\n" + "\t{second_comparand_infile_dir}/results/" + "{second_comparand_infile_name}.duplicates.out.txt\n" + "- Determines if there any graphs that appear in the first .g6 " + "file that do not appear in the second .g6 file, and vice versa; " + "outputs to files with paths:\n" + "\t{first_comparand_infile_dir}/results/graphs_in_" + "{first_comparand_infile_name}_not_in_" + "{second_comparand_infile_name}.g6\n" + "\t{second_comparand_infile_dir}/results/graphs_in_" + "{second_comparand_infile_name}_not_in_" + "{first_comparand_infile_name}.g6\n" + "- Records graphs that occur in both files but which appear on " + "different line numbers; outputs to a file with path:\n" + "\t{first_comparand_infile_dir}/results/graphs_in_" + "{first_comparand_infile_name}_and_" + "{second_comparand_infile_name}.txt", + ) + parser.add_argument( + "--first_comparand", + "-f", + type=Path, + help="The first .g6 file to compare.", + metavar="FIRST_COMPARAND.g6", + required=True, + ) + parser.add_argument( + "--second_comparand", + "-s", + type=Path, + help="The second .g6 file to compare.", + metavar="SECOND_COMPARAND.g6", + required=True, + ) + + args = parser.parse_args() + + try: + g6_diff_finder = G6DiffFinder( + args.first_comparand, args.second_comparand + ) + except Exception as e: + raise G6DiffFinderException( + "Unable to initialize G6DiffFinder with given input files." + ) from e + + try: + g6_diff_finder.output_duplicates() + except Exception as e: + raise G6DiffFinderException( + "Unable to output duplicates for given input files." + ) from e + + try: + g6_diff_finder.graph_set_diff() + except Exception as e: + raise G6DiffFinderException( + "Failed to discern diff between two .g6 input files." + ) from e + + try: + g6_diff_finder.graph_set_intersection_with_different_line_nums() + except Exception as e: + raise G6DiffFinderException( + "Failed to determine set intersection of two .g6 input files." + ) from e diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_generation_and_comparison_driver.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_generation_and_comparison_driver.py new file mode 100644 index 000000000..7ce9e615d --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/g6_generation_and_comparison_driver.py @@ -0,0 +1,1089 @@ +"""Generate .g6 files, run planarity, identify discrepancies, and perform diffs + +Classes: + G6GenerationAndComparisonDriver + +Functions: + parse_range(value: str) -> tuple +""" # pylint: disable=too-many-lines + +#!/usr/bin/env python + +__all__ = [] + +import sys +import os +import shutil +import argparse +from pathlib import Path +import subprocess +from typing import Optional +import json + +from graph_generation_orchestrator import distribute_geng_workload +from planarity_testAllGraphs_orchestrator import ( + distribute_planarity_testAllGraphs_workload, +) +from g6_diff_finder import G6DiffFinder, G6DiffFinderException +from planaritytesting_utils import ( + PLANARITY_ALGORITHM_SPECIFIERS, + max_num_edges_for_order, + is_path_to_executable, + parse_range, +) +from planarity_testAllGraphs_output_parsing import process_file_contents + + +class G6GenerationAndComparisonDriver: + """Generates .g6 files, runs planarity, and identifies discrepancies""" + + def __init__( + self, + geng_path: Path, + orders: tuple, + planarity_path: Optional[Path] = None, + planarity_backup_path: Optional[Path] = None, + output_parent_dir: Optional[Path] = None, + ): # pylint: disable=too-many-arguments + """Initializes G6GenerationAndComparisonDriver instance. + + Args: + geng_path: The path to the nauty geng executable used to generate + .g6 graph files for each order and edge-count + orders: A tuple of ints corresponding to the graph orders for which + we wish to perform the graph generation and comparison + planarity_path: The path to the edge-addition-planarity-suite + executable + planarity_backup_path: The path to the planarity-backup executable + (optional; used to generate the makeg .g6 files) + output_parent_dir: Directory to which you wish to output results + + Raises: + argparse.ArgumentError: Re-raises if any of the arguments were + invalid + """ + try: + ( + geng_path, + orders, + planarity_path, + planarity_backup_path, + output_parent_dir, + ) = self._validate_and_normalize_input( + geng_path, + orders, + planarity_path, + planarity_backup_path, + output_parent_dir, + ) + except argparse.ArgumentError as e: + raise argparse.ArgumentError( + argument=None, + message="Invalid parameters; unable to proceed with " + "generation and comparison.", + ) from e + + self.geng_path = geng_path + self.orders = orders + self.planarity_path = planarity_path + self.planarity_backup_path = planarity_backup_path + self.output_parent_dir = output_parent_dir + + self.planarity_discrepancies = {} + + def _validate_and_normalize_input( # pylint: disable=too-many-arguments + self, + geng_path: Path, + orders: tuple, + planarity_path: Optional[Path] = None, + planarity_backup_path: Optional[Path] = None, + output_parent_dir: Optional[Path] = None, + ) -> tuple[Path, tuple[int, ...], Path, Optional[Path], Path]: + """Validates G6GenerationAndComparisonDriver initialization values + + Args: + geng_path: Path to geng executable + orders: Tuple containing orders of graphs for which you wish to + generate the .g6 files + planarity_path: Path to planarity executable; defaults to: + Release/planarity + planarity_backup_path: Path to planarity-backup executable, or None + output_parent_dir: Path to directory to which you wish to output + .g6 files and comparison results; defaults to: + TestSupport/results/g6_generation_and_comparison_driver/ + + Returns: + A tuple comprised of the valid and normalized input in their + original order. + Raises: + argparse.ArgumentTypeError: If any of the arguments were invalid + """ + if not is_path_to_executable(geng_path): + raise argparse.ArgumentTypeError( + f"Path for geng executable '{geng_path}' does not correspond " + "to an executable." + ) + + if not orders: + raise argparse.ArgumentTypeError("No graph orders given.") + + if any( + order + for order in orders + if order < 2 + if order > 12 + if not isinstance(order, int) + ): + raise argparse.ArgumentTypeError( + "Desired graph orders must be integers between 2 an 12 " + "inclusive." + ) + + planarity_project_root = ( + Path(sys.argv[0]).resolve().parent.parent.parent + ) + + if not planarity_path: + planarity_path = Path.joinpath( + planarity_project_root, "Release", "planarity" + ) + + if not is_path_to_executable(planarity_path): + raise argparse.ArgumentTypeError( + f"Path for planarity executable '{planarity_path}' does not " + "correspond to an executable." + ) + + if planarity_backup_path and not is_path_to_executable( + planarity_backup_path + ): + raise argparse.ArgumentTypeError( + "Path for planarity_backup executable " + f"'{planarity_backup_path}' does not correspond to an " + "executable." + ) + + if not output_parent_dir: + output_parent_dir = Path.joinpath( + planarity_project_root, + "TestSupport", + "results", + "g6_generation_and_comparison_driver", + ) + Path.mkdir(output_parent_dir, parents=True, exist_ok=True) + for order in orders: + candidate_output_dir = Path.joinpath( + output_parent_dir, f"{order}" + ) + if Path.is_dir(candidate_output_dir): + shutil.rmtree(candidate_output_dir) + + if ( + not isinstance(output_parent_dir, Path) + or not output_parent_dir.is_dir() + ): + raise argparse.ArgumentTypeError( + "Output directory path is invalid." + ) + + return ( + geng_path, + orders, + planarity_path, + planarity_backup_path, + output_parent_dir, + ) + + def generate_g6_files(self): + """Generate .g6 files using nauty geng and planarity-backup's makeg + + For each graph order in self.orders, uses the graph generation + orchestrator to generate the geng .g6 and geng canonical .g6 files (all + graphs of a single edge-count per file), then if the path to the + planarity-backup executable was provided, calls this executable for + each edge-count from 0 to (N * (N - 1)) / 2 to generate the makeg .g6 + and makeg canonical .g6 files. + """ + for order in self.orders: + g6_output_dir_for_order = Path.joinpath( + self.output_parent_dir, f"{order}" + ) + self._generate_geng_g6_files_for_order( + order, g6_output_dir_for_order + ) + + if self.planarity_backup_path: + for num_edges in range(max_num_edges_for_order(order) + 1): + self._generate_makeg_g6_files_for_order_and_num_edges( + order, num_edges + ) + + def _generate_geng_g6_files_for_order( + self, order: int, geng_g6_output_dir_for_order: Path + ): + """Parallelize geng calls + + Args: + order: The graph order for which we wish to generate all graphs + for each edge-count + geng_g6_output_dir_for_order: Directory to which we wish to output + the geng .g6 and geng canonical .g6 graph files + """ + distribute_geng_workload( + geng_path=self.geng_path, + canonical_files=False, + order=order, + output_dir=geng_g6_output_dir_for_order, + ) + + distribute_geng_workload( + geng_path=self.geng_path, + canonical_files=True, + order=order, + output_dir=geng_g6_output_dir_for_order, + ) + + def _generate_makeg_g6_files_for_order_and_num_edges( + self, order: int, num_edges: int, command: str = "3" + ): + """Generate makeg .g6 and makeg canonical .g6 files + + The makeg .g6 and makeg canonical .g6 files generated have paths: + {self.output_parent_dir}/{order}/ + n{order}.m{num_edges}.makeg(.canonical)?.g6 + Which is an intermediate directory required for the use of the + planarity_testAllGraphs_orchestrator (which runs on all .g6 files in + the given root directory matching a specific pattern indicated by the + canonical_files and makeg_g6 flags) + + The output files of planarity-backup's Test All Graphs for the + given algorithm command specifier have paths: + {self.output_parent_dir}/{order}/planarity_results/{command}/ + {num_edges}/planarity-backup-output_n{order}.m{num_edges}.makeg(.canonical)?.{command}.out.txt + + Args: + order: Order of graphs to generate + num_edges: Restricts .g6 output file to contain only graphs with + the desired edge-count + command: Algorithm command specifier to use when testing all graphs + of the given order and edge-count (Defaults to '3' + corresponding to K_{3, 3} search) + """ # pylint: disable=line-too-long + g6_output_dir_for_order_and_edgecount = Path.joinpath( + self.output_parent_dir, f"{order}" + ) + Path.mkdir( + g6_output_dir_for_order_and_edgecount, parents=True, exist_ok=True + ) + + planarity_backup_outfile_dir = Path.joinpath( + self.output_parent_dir, + f"{order}", + "planarity_results", + f"{command}", + f"{num_edges}", + ) + Path.mkdir(planarity_backup_outfile_dir, parents=True, exist_ok=True) + + self._generate_makeg_g6_file_for_order_and_num_edges( + g6_output_dir_for_order_and_edgecount, + planarity_backup_outfile_dir, + order, + num_edges, + False, + command, + ) + self._generate_makeg_g6_file_for_order_and_num_edges( + g6_output_dir_for_order_and_edgecount, + planarity_backup_outfile_dir, + order, + num_edges, + True, + command, + ) + + def _generate_makeg_g6_file_for_order_and_num_edges( # pylint: disable=too-many-arguments + self, + g6_output_dir_for_order_and_edgecount: Path, + planarity_backup_outfile_dir: Path, + order: int, + num_edges: int, + canonical_files: bool, + command: str = "3", + ): + """Run planarity-backup executable to generate makeg .g6 files + + Runs the old planarity-backup code with the given algorithm command + specifier (defaults to K_{3, 3} search) to test all graphs of a given + order and edge-count to determine the number of OKs (no K_{3, 3}) vs. + number of NONEMBEDDABLEs (contains a K_{3, 3}) + + The graphs generated by makeg are written to file: + {g6_output_dir_for_order_and_edgecount}/ + n{order}.m{num_edges}.makeg(.canonical)?.g6 + + Args: + g6_output_dir_for_order_and_edgecount: (Intermediate) directory to + which you wish to output makeg .g6 files for the given order + and edge-count + planarity_backup_outfile_dir: (Intermediate) directory to which you + wish to output the results of running the planarity-backup + executable for the given command specifier on all graphs of the + given order and edge-count + order: Order of graphs to generate + num_edges: Restricts .g6 output file to contain only graphs with + the desired edge-count + canonical_files: Indicates whether or not makeg should generate + canonical .g6 graphs + command: Algorithm command specifier to use when testing all graphs + of the given order and edge-count (Defaults to '3' + corresponding to K_{3, 3} search) + """ + canonical_ext = ".canonical" if canonical_files else "" + planarity_backup_outfile_name = Path.joinpath( + planarity_backup_outfile_dir, + f"planarity-backup-output_n{order}.m{num_edges}." + + f"makeg{canonical_ext}.{command}.out.txt", + ) + with open( + planarity_backup_outfile_name, "w", encoding="utf-8" + ) as makeg_outfile: + planarity_backup_args = [ + f"{self.planarity_backup_path}", + "-gen", + f"{g6_output_dir_for_order_and_edgecount}", + f"-{command}", + f"{order}", + f"{num_edges}", + f"{num_edges}", + ] + if canonical_files: + planarity_backup_args.insert(4, "-l") + subprocess.run( + planarity_backup_args, + stdout=makeg_outfile, + stderr=subprocess.PIPE, + check=False, + ) + + def run_planarity(self): + """Runs planarity on all .g6 files + + For each graph order in self.orders, runs planarity Test All Graphs + for every algorithm command specifier on all geng .g6 and geng + canonical .g6 files: + {self.output_parent_dir}/{order}/ + n{order}.m{num_edges}(.canonical)?.g6 + + If a path to the planarity-backup executable was provided, runs + planarity Test All Graphs for every algorithm command specifier on all + makeg .g6 and makeg canonical .g6 files: + {self.output_parent_dir}/{order}/ + n{order}.m{num_edges}(.makeg)?(.canonical)?.g6 + """ + for order in self.orders: + geng_g6_output_dir_for_order = Path.joinpath( + self.output_parent_dir, f"{order}" + ) + planarity_output_dir_for_order = Path.joinpath( + self.output_parent_dir, "results", f"{order}" + ) + self._run_planarity_on_g6_files_for_order( + order, + geng_g6_output_dir_for_order, + planarity_output_dir_for_order, + makeg_g6=False, + ) + + if self.planarity_backup_path: + self._run_planarity_on_g6_files_for_order( + order, + geng_g6_output_dir_for_order, + planarity_output_dir_for_order, + makeg_g6=True, + ) + + def _run_planarity_on_g6_files_for_order( + self, + order: int, + g6_output_dir_for_order: Path, + planarity_output_dir_for_order: Path, + makeg_g6: bool, + ): + """Parallelize running planarity TestAllGraphs for all commands + + Args: + order: Order of graphs in .g6 files on which planarity's Test All + Graphs shall be run for the given command + g6_output_dir_for_order: Directory containing the input .g6 files, + one file per num_edges for a given graph order. + planarity_output_dir_for_order: Directory to which results of + running planarity on each .g6 file containing all graphs of a + specific num_edges for the given graph order shall be output + makeg_g6: Flag to indicate that the .g6 files taken as input were + generated using planarity-backup's makeg + """ + distribute_planarity_testAllGraphs_workload( + planarity_path=self.planarity_path, + canonical_files=False, + makeg_g6=makeg_g6, + order=order, + input_dir=g6_output_dir_for_order, + output_dir=planarity_output_dir_for_order, + ) + + distribute_planarity_testAllGraphs_workload( + planarity_path=self.planarity_path, + canonical_files=True, + makeg_g6=makeg_g6, + order=order, + input_dir=g6_output_dir_for_order, + output_dir=planarity_output_dir_for_order, + ) + + def reorganize_files(self): + """Reorganize .g6 and planarity Test All Graphs output files + + After run_planarity(), the various .g6 files and planarity Test All + Graphs output files must be reorganized to make subsequent processing + easier. + """ + for order in self.orders: + orig_geng_g6_output_dir_for_order = Path.joinpath( + self.output_parent_dir, f"{order}" + ) + for num_edges in range(max_num_edges_for_order(order) + 1): + self._move_g6_files( + order, num_edges, orig_geng_g6_output_dir_for_order + ) + self._move_planarity_output_files( + order, num_edges, orig_geng_g6_output_dir_for_order + ) + + shutil.rmtree(Path.joinpath(self.output_parent_dir, "results")) + + def _move_g6_files( + self, order: int, num_edges: int, g6_output_dir_for_order: Path + ): + """Sort generated .g6 files into sub-dirs by graph edge-count + + After moving, {self.output_parent_dir}/{order}/graphs/{num_edges}/ + contains: + n{order}.m{num_edges}.g6 + n{order}.m{num_edges}.canonical.g6 + If planarity-backup path provided, also shall include: + n{order}.m{num_edges}.makeg.g6 + n{order}.m{num_edges}.makeg.canonical.g6 + + Args: + order: Order of graphs contained in the .g6 files to move + num_edges: Only move the .g6 files that contain all graphs of a + specific edge-count + g6_output_dir_for_order: Original directory containing the .g6 + files produced by generate_g6_files() + """ + g6_output_dir_for_order_and_edgecount = Path.joinpath( + g6_output_dir_for_order, "graphs", f"{num_edges}" + ) + + Path.mkdir( + g6_output_dir_for_order_and_edgecount, parents=True, exist_ok=True + ) + + geng_g6_outfile_name = f"n{order}.m{num_edges}.g6" + self._move_file( + g6_output_dir_for_order, + geng_g6_outfile_name, + g6_output_dir_for_order_and_edgecount, + ) + + geng_canonical_g6_outfile_name = f"n{order}.m{num_edges}.canonical.g6" + self._move_file( + g6_output_dir_for_order, + geng_canonical_g6_outfile_name, + g6_output_dir_for_order_and_edgecount, + ) + + if self.planarity_backup_path: + makeg_g6_outfile_name = f"n{order}.m{num_edges}.makeg.g6" + self._move_file( + g6_output_dir_for_order, + makeg_g6_outfile_name, + g6_output_dir_for_order_and_edgecount, + ) + + makeg_canonical_g6_outfile_name = ( + f"n{order}.m{num_edges}.makeg.canonical.g6" + ) + self._move_file( + g6_output_dir_for_order, + makeg_canonical_g6_outfile_name, + g6_output_dir_for_order_and_edgecount, + ) + + def _move_planarity_output_files( + self, order: int, num_edges: int, new_output_dir_for_order: Path + ): + """Sort planarity output files into sub-dirs by command and edge-count + + After moving, {order}/planarity_results/{command}/{num_edges}/ + contains: + n{order}.m{num_edges}.{command}.out.txt + n{order}.m{num_edges}.canonical.{command}.out.txt + If planarity-backup path provided, also shall include: + n{order}.m{num_edges}.makeg.{command}.out.txt + n{order}.m{num_edges}.makeg.canonical.{command}.out.txt + + Args: + order: Order of graphs contained in the .g6 files taken as input to + planarity Test All Graphs + num_edges: Only move the planarity output files corresponding to + specific edge-count + new_output_dir_for_order: Directory under which new subdirectory, + planarity_results/, should be created + """ + for command in PLANARITY_ALGORITHM_SPECIFIERS(): + orig_planarity_output_dir = Path.joinpath( + self.output_parent_dir, "results", f"{order}", f"{command}" + ) + new_planarity_output_dir_for_order_and_edgecount = Path.joinpath( + new_output_dir_for_order, + "planarity_results", + f"{command}", + f"{num_edges}", + ) + + Path.mkdir( + new_planarity_output_dir_for_order_and_edgecount, + parents=True, + exist_ok=True, + ) + + g6_planarity_outfile_name = ( + f"n{order}.m{num_edges}.{command}.out.txt" + ) + self._move_file( + orig_planarity_output_dir, + g6_planarity_outfile_name, + new_planarity_output_dir_for_order_and_edgecount, + ) + + canonical_g6_planarity_outfile_name = ( + f"n{order}.m{num_edges}.canonical.{command}.out.txt" + ) + self._move_file( + orig_planarity_output_dir, + canonical_g6_planarity_outfile_name, + new_planarity_output_dir_for_order_and_edgecount, + ) + + if self.planarity_backup_path: + makeg_g6_planarity_outfile_name = ( + f"n{order}.m{num_edges}.makeg.{command}.out.txt" + ) + self._move_file( + orig_planarity_output_dir, + makeg_g6_planarity_outfile_name, + new_planarity_output_dir_for_order_and_edgecount, + ) + + canonical_makeg_g6_planarity_outfile_name = ( + f"n{order}.m{num_edges}.makeg.canonical.{command}.out.txt" + ) + self._move_file( + orig_planarity_output_dir, + canonical_makeg_g6_planarity_outfile_name, + new_planarity_output_dir_for_order_and_edgecount, + ) + + def _move_file(self, src_dir: Path, filename: str, dest_dir: Path): + """Moves {src_dir}/{filename} to {dest_dir}/ + + If a file with name {filename} occurs in {dest_dir}, we os.remove() it + before we shutil.move() the file to the {dest_dir}. + + Args: + src_dir: Directory containing the file to move + filename: Name of the file to be moved + dest_dir: Directory to which we wish to move the file + """ + src_path = Path.joinpath(src_dir, filename) + dest_path = Path.joinpath(dest_dir, filename) + if Path.is_file(dest_path): + os.remove(dest_path) + + shutil.move(src_path, dest_dir) + + def find_planarity_discrepancies(self): + """Find graph order, edge-count, and command w/ planarity disagreements + + If the sub-dicts for geng_g6, makeg_g6, geng_canonical_g6, and + makeg_canonical_g6 disagree for a given order, num_edges, and command, + then the structure will be as follows: + { + f"{order}": { + f"{num_edges}" : { + f"{command}: { + "geng_g6": { + 'numGraphs': numGraphs_from_file, + 'numOK': numOK_from_file, + 'numNONEMBEDDABLE': numNONEMBEDDABLE_from_file, + }, + "makeg_g6": { + 'numGraphs': numGraphs_from_file, + 'numOK': numOK_from_file, + 'numNONEMBEDDABLE': numNONEMBEDDABLE_from_file, + }, + "geng_canonical_g6": { + 'numGraphs': numGraphs_from_file, + 'numOK': numOK_from_file, + 'numNONEMBEDDABLE': numNONEMBEDDABLE_from_file, + }, + "makeg_canonical_g6": { + 'numGraphs': numGraphs_from_file, + 'numOK': numOK_from_file, + 'numNONEMBEDDABLE': numNONEMBEDDABLE_from_file, + } + } + ... + } + ... + } + ... + } + """ + for order in self.orders: + planarity_output_dir = Path.joinpath( + self.output_parent_dir, f"{order}", "planarity_results" + ) + self.planarity_discrepancies[order] = {} + for num_edges in range(max_num_edges_for_order(order) + 1): + self.planarity_discrepancies[order][num_edges] = {} + for command in PLANARITY_ALGORITHM_SPECIFIERS(): + self.planarity_discrepancies[order][num_edges][ + command + ] = {} + + planarity_output_dir_for_order_and_edgecount = ( + Path.joinpath( + planarity_output_dir, f"{command}", f"{num_edges}" + ) + ) + + geng_g6_output = Path.joinpath( + planarity_output_dir_for_order_and_edgecount, + f"n{order}.m{num_edges}.{command}.out.txt", + ) + self._extract_planarity_results( + geng_g6_output, order, num_edges, command, "geng_g6" + ) + + geng_canonical_g6_output = Path.joinpath( + planarity_output_dir_for_order_and_edgecount, + f"n{order}.m{num_edges}.canonical.{command}.out.txt", + ) + self._extract_planarity_results( + geng_canonical_g6_output, + order, + num_edges, + command, + "geng_canonical_g6", + ) + + if self.planarity_backup_path: + makeg_g6_output = Path.joinpath( + planarity_output_dir_for_order_and_edgecount, + f"n{order}.m{num_edges}.makeg.{command}.out.txt", + ) + self._extract_planarity_results( + makeg_g6_output, + order, + num_edges, + command, + "makeg_g6", + ) + + makeg_canonical_g6_output = Path.joinpath( + planarity_output_dir_for_order_and_edgecount, + f"n{order}.m{num_edges}.makeg.canonical." + + f"{command}.out.txt", + ) + self._extract_planarity_results( + makeg_canonical_g6_output, + order, + num_edges, + command, + "makeg_canonical_g6", + ) + + if ( + self.planarity_discrepancies[order][num_edges][ + command + ]["geng_g6"] + == self.planarity_discrepancies[order][num_edges][ + command + ]["makeg_g6"] + == self.planarity_discrepancies[order][num_edges][ + command + ]["geng_canonical_g6"] + == self.planarity_discrepancies[order][num_edges][ + command + ]["makeg_canonical_g6"] + ): + del self.planarity_discrepancies[order][num_edges][ + command + ] + else: + if ( + self.planarity_discrepancies[order][num_edges][ + command + ]["geng_g6"] + == self.planarity_discrepancies[order][num_edges][ + command + ]["geng_canonical_g6"] + ): + del self.planarity_discrepancies[order][num_edges][ + command + ] + + if not self.planarity_discrepancies[order][num_edges]: + del self.planarity_discrepancies[order][num_edges] + planarity_discrepancies_for_order_outfile_path = Path.joinpath( + planarity_output_dir, f"n{order}.planarity_discrepancies.txt" + ) + with open( + planarity_discrepancies_for_order_outfile_path, + "w", + encoding="utf-8", + ) as planarity_discrepancies_for_order_outfile: + planarity_discrepancies_for_order_outfile.write( + "Discrepancies in planarity output results for order " + f"{order} by edge-count and algorithm specifier:\n" + "======\n" + ) + planarity_discrepancies_for_order_outfile.write( + json.dumps(self.planarity_discrepancies[order], indent=4) + ) + + def _extract_planarity_results( + self, + planarity_outfile: Path, + order: int, + num_edges: int, + command: str, + file_type: str, + ): + """Use planarity_testAllGraphs_output_parsing to parse planarity output + + Args: + planarity_outfile: The output of having run planarity Test All + Graphs for the given algorithm command specifier for all graphs + of a specific order and num_edges + order: Order of graphs in .g6 file on which planarity's Test All + Graphs was run for the given command to produce the outfile + num_edges: Number of edges for graphs in the .g6 file + command: Algorithm command specifier + file_type: One of geng_g6, makeg_g6, geng_canonical_g6, or + makeg_canonical_g6, indicating the type of the original .g6 + input file + """ + ( + _, + _, + numGraphs_from_file, + numOK_from_file, + numNONEMBEDDABLE_from_file, + _, + ) = process_file_contents(planarity_outfile, command) + + self.planarity_discrepancies[order][num_edges][command][file_type] = { + "numGraphs": numGraphs_from_file, + "numOK": numOK_from_file, + "numNONEMBEDDABLE": numNONEMBEDDABLE_from_file, + } + + def get_planarity_discrepancy_g6_diffs( + self, + ): # pylint: disable=too-many-locals + """Get .g6 file diffs only if discrepancies exist in planarity output + + Since discrepancies in planarity results for all graphs of a given + order and edge-count must only occur on graphs that appear in one .g6 + file but not the other, it was useful to use the G6DiffFinder to narrow + down the set of graphs for which there occurred more OKs vs. + NONEMBEDDABLEs. + + The dictionary self.planarity_discrepancies is populated during the + course of find_planarity_discrepancies(). + + To avoid duplicating work, the dictionary diffs_performed maps graph + orders to a set of edge-counts for which we have already + performed the diffs between the various .g6 file combinations: + { + f"{order}": { + e_0, e_1, ..., e_k + } + ... + } + + The logs in + {self.output_parent_dir}/{order}/g6_diff_finder_logs/ + contain log.info messages emitted by the G6DiffFinder when comparing: + + - geng .g6 vs. geng canonical .g6 + - G6DiffFinder.n{order}.geng_vs_geng-canonical.log + - geng .g6 vs. makeg .g6 + - G6DiffFinder.n{order}.geng_vs_makeg.log + - geng .g6 vs. makeg canonical .g6 + - G6DiffFinder.n{order}.geng_vs_makeg-canonical.log + - geng canonical .g6 vs. makeg canonical .g6 + - G6DiffFinder.n{order}.geng-canonical_vs_makeg-canonical.log + - makeg .g6 vs. makeg canonical .g6 + - G6DiffFinder.n{order}.makeg_vs_makeg-canonical.log + + Up to 10 diff files will be output to the directory + {self.output_parent_dir}/{order}/{graphs}/{num_edges}/{results}/ + - geng .g6 vs. geng canonical .g6 corresponds to the files: + - graphs_in_n{order}.m{num_edges}_not_in_n{order}.m{num_edges}.canonical.g6 + - graphs_in_n{order}.m{num_edges}.canonical_not_in_n{order}.m{num_edges}.g6 + - geng .g6 vs. makeg .g6 corresponds to the files: + - graphs_in_n{order}.m{num_edges}_not_in_n{order}.m{num_edges}.makeg.g6 + - graphs_in_n{order}.m{num_edges}.makeg_not_in_n{order}.m{num_edges}.g6 + - geng .g6 vs. makeg canonical .g6 corresponds to the files: + - graphs_in_n{order}.m{num_edges}_not_in_n{order}.m{num_edges}.makeg.canonical.g6 + - graphs_in_n{order}.m{num_edges}.makeg.canonical_not_in_n{order}.m{num_edges}.g6 + - geng canonical .g6 vs. makeg canonical .g6 corresponds to the files: + - graphs_in_n{order}.m{num_edges}.canonical_not_in_n{order}.m{num_edges}.makeg.canonical.g6 + - graphs_in_n{order}.m{num_edges}.makeg.canonical_not_in_n{order}.m{num_edges}.canonical.g6 + - makeg .g6 vs. makeg canonical .g6 corresponds to the files: + - graphs_in_n{order}.m{num_edges}.makeg_not_in_n{order}.m{num_edges}.makeg.canonical.g6 + - graphs_in_n{order}.m{num_edges}.makeg.canonical_not_in_n{order}.m{num_edges}.makeg.g6 + """ # pylint: disable=line-too-long + diffs_performed = {} + for ( + order, + discrepancies_for_order, + ) in self.planarity_discrepancies.items(): + if not diffs_performed.get(order): + diffs_performed[order] = set() + + output_dir_for_order = Path.joinpath( + self.output_parent_dir, f"{order}" + ) + log_dir_for_order = Path.joinpath( + output_dir_for_order, + "g6_diff_finder_logs", + ) + Path.mkdir(log_dir_for_order, parents=True, exist_ok=True) + + log_path_for_geng_g6_vs_geng_canonical_g6 = Path.joinpath( + log_dir_for_order, + f"G6DiffFinder.n{order}.geng_vs_geng-canonical.log", + ) + log_path_for_makeg_g6_vs_makeg_canonical_g6 = Path.joinpath( + log_dir_for_order, + f"G6DiffFinder.n{order}.makeg_vs_makeg-canonical.log", + ) + log_path_for_geng_g6_vs_makeg_g6 = Path.joinpath( + log_dir_for_order, f"G6DiffFinder.n{order}.geng_vs_makeg.log" + ) + log_path_for_geng_canonical_g6_vs_makeg_canonical_g6 = ( + Path.joinpath( + log_dir_for_order, + f"G6DiffFinder.n{order}." + + "geng-canonical_vs_makeg-canonical.log", + ) + ) + log_path_for_geng_g6_vs_makeg_canonical_g6 = Path.joinpath( + log_dir_for_order, + f"G6DiffFinder.n{order}.geng_vs_makeg-canonical.log", + ) + + for num_edges in discrepancies_for_order.keys(): + if num_edges in diffs_performed[order]: + continue + + diffs_performed[order].add(num_edges) + + g6_files_for_order_and_edgecount = Path.joinpath( + output_dir_for_order, "graphs", f"{num_edges}" + ) + geng_g6_path = Path.joinpath( + g6_files_for_order_and_edgecount, + f"n{order}.m{num_edges}.g6", + ) + geng_canonical_g6_path = Path.joinpath( + g6_files_for_order_and_edgecount, + f"n{order}.m{num_edges}.canonical.g6", + ) + makeg_g6_path = Path.joinpath( + g6_files_for_order_and_edgecount, + f"n{order}.m{num_edges}.makeg.g6", + ) + makeg_canonical_g6_path = Path.joinpath( + g6_files_for_order_and_edgecount, + f"n{order}.m{num_edges}.makeg.canonical.g6", + ) + + self._get_diffs( + geng_g6_path, + geng_canonical_g6_path, + log_path_for_geng_g6_vs_geng_canonical_g6, + ) + + if self.planarity_backup_path: + self._get_diffs( + geng_g6_path, + makeg_g6_path, + log_path_for_geng_g6_vs_makeg_g6, + ) + self._get_diffs( + geng_g6_path, + makeg_canonical_g6_path, + log_path_for_geng_g6_vs_makeg_canonical_g6, + ) + # Don't bother with geng canonical vs. makeg, because makeg + # and geng are so similar; only do the other 4 possible + # pairings (4C2 - 1 = 5 cases, which each produce 2 files) + self._get_diffs( + geng_canonical_g6_path, + makeg_canonical_g6_path, + log_path_for_geng_canonical_g6_vs_makeg_canonical_g6, + ) + self._get_diffs( + makeg_g6_path, + makeg_canonical_g6_path, + log_path_for_makeg_g6_vs_makeg_canonical_g6, + ) + + def _get_diffs( + self, + first_comparand_infile: Path, + second_comparand_infile: Path, + log_path: Path, + ): + """Uses G6DiffFinder to output set differences of two .g6 infiles + + Args: + first_comparand_infile: Path to first .g6 comparand infile + second_comparand_infile: Path to second .g6 comparand infile + log_path: Path to which logs should be written + + Raises: + G6DiffFinderException: If an error occurred during the processing + of the two .g6 comparand infiles + """ + try: + g6_diff_finder = G6DiffFinder( + first_comparand_infile, second_comparand_infile, log_path + ) + except Exception as g6_diff_finder_error: + raise G6DiffFinderException( + "Unable to initialize G6DiffFinder with given input files: " + f"'{first_comparand_infile}', '{second_comparand_infile}'" + ) from g6_diff_finder_error + + try: + g6_diff_finder.graph_set_diff() + except BaseException as e: + raise G6DiffFinderException( + "Failed to discern diff between two .g6 input files." + ) from e + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="G6 File Generation and Comparison Tool\n\n" + "For each graph order in the specified range, a child directory " + "of the output directory will be created named 'n{order}'. This " + "directory will contain subdirectories:\n" + "- graphs/\n" + "\t- For each edge-count from 0 to (N * (N - 1)) / 2, a " + "directory is created to contain:\n" + "\tn{order}.m{num_edges}.g6\n" + "\tn{order}.m{num_edges}.canonical.g6\n" + "\tn{order}.m{num_edges}.makeg.g6 (req. planarity-backup " + "\tpath)\n" + "\tn{order}.m{num_edges}.makeg.canonical.g6 (req. " + "planarity-backup path)\n" + "\t- results/ which will contain the diffs of these .g6 " + " files, pairs of files:\n" + "\t\t- graphs_in_{first_infile}_not_in_{second_infile}.g6\n" + "\t\t- graphs_in_{second_infile}_not_in_{first_infile}.g6\n" + "\tThese pairs should contain the same number of graphs\n" + "- planarity_results/\n" + "\t- For each graph algorithm command specifier, there will be " + "a subdirectory containing one subdirectory for each edge-count " + "(0 to (N * (N - 1)) / 2), which will contain files:\n" + "\tn{order}.m{num_edges}.{command}.out.txt\n" + "\tn{order}.m{num_edges}.canonical.{command}.out.txt\n" + "\tn{order}.m{num_edges}.makeg.{command}.out.txt\n" + "\tn{order}.m{num_edges}.makeg.canonical.{command}.out.txt\n" + "- g6_diff_finder_logs/\n" + "\t- Will contain logs for each of the following comparisons:\n" + "\tG6DiffFinder.n{order}.geng_vs_geng-canonical.log\n" + "\tG6DiffFinder.n{order}.geng_vs_makeg.log\n" + "\tG6DiffFinder.n{order}.geng_vs_makeg-canonical.log\n" + "\tG6DiffFinder.n{order}.geng-canonical_vs_makeg-canonical.log\n" + "\tG6DiffFinder.n{order}.makeg_vs_makeg-canonical.log\n", + ) + parser.add_argument( + "-g", + "--gengpath", + type=Path, + required=True, + help="Path to nauty geng executable", + metavar="PATH_TO_GENG_EXECUTABLE", + ) + parser.add_argument( + "-n", + "--orders", + type=parse_range, + required=True, + help="Order(s) of graphs for which you wish to generate .g6 files", + metavar="X[,Y]", + ) + parser.add_argument( + "-p", + "--planaritypath", + type=Path, + required=False, + help="Path to planarity executable", + metavar="PATH_TO_PLANARITY_EXECUTABLE", + ) + parser.add_argument( + "-b", + "--planaritybackuppath", + type=Path, + required=False, + help="Path to planarity-backup executable (optional; not public!)", + metavar="PATH_TO_PLANARITY_BACKUP_EXECUTABLE", + ) + parser.add_argument( + "-o", + "--outputdir", + type=Path, + default=None, + metavar="OUTPUT_DIR_PATH", + help="Parent directory under which subdirectories will be created " + "for output results. If none provided, defaults to:\n" + "\tTestSupport/results/g6_generation_and_comparison_driver/", + ) + + args = parser.parse_args() + + g6_generation_and_comparison_driver = G6GenerationAndComparisonDriver( + geng_path=args.gengpath, + orders=args.orders, + planarity_path=args.planaritypath, + planarity_backup_path=args.planaritybackuppath, + output_parent_dir=args.outputdir, + ) + + g6_generation_and_comparison_driver.generate_g6_files() + g6_generation_and_comparison_driver.run_planarity() + g6_generation_and_comparison_driver.reorganize_files() + g6_generation_and_comparison_driver.find_planarity_discrepancies() + g6_generation_and_comparison_driver.get_planarity_discrepancy_g6_diffs() diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph.py new file mode 100644 index 000000000..69ac45765 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph.py @@ -0,0 +1,303 @@ +"""Pure Python module containing classes to represent graphs as adjacency lists + +Classes: + GraphError + Graph +""" + +__all__ = ["Graph", "GraphError"] + +from copy import deepcopy +from typing import Optional + + +class GraphError(BaseException): + """Signals issues encountered while managing a Graph object""" + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class Graph: + """A simple graph datastructure containing the order graph adjacency list""" + + def __init__( + self, + order: int = 0, + graph_adj_list_repr: Optional[list[list[int]]] = None, + ) -> None: + """Constructor for Graph class + + Had to do some fancy footwork to support deepcopy() machinery: if you + try to create an instance with only the graph_adj_list_repr and don't + supply the order, the order is inferred from the number of elements of + the outer list, and then the graph adjacency-list representation is + validated. If you supply both the order and adjacency list, then we + ensure the order and graph adjacency list representation make sense. + If only the graph order is supplied, then a list of lists is + initialized, which will be subsequently populated by repeated calls to + self.add_edge(). + + Args: + order: The order of the graph + graph_adj_list_repr: A list of lists of integers comprising the + graph's adjacency list representation + Raises: + GraphError: If supplied order and graph_adj_list_repr do not agree, + re-raises from Graph._validate_adj_list(), or if graph order + is not in closed interval [2, 12]. + """ + if order == 0: + if graph_adj_list_repr: + order_implied_by_adj_list = len(graph_adj_list_repr) + try: + Graph._validate_adj_list( + order_implied_by_adj_list, graph_adj_list_repr + ) + except GraphError as e: + raise GraphError( + "Unable to initialize graph from given adjacency list." + ) from e + + self.order = order_implied_by_adj_list + self.graph_adj_list_repr = graph_adj_list_repr + else: + if order < 2 or order > 12: + raise GraphError( + f"Graph order '{order}' is not in closed interval [2, 12]." + ) + if graph_adj_list_repr: + try: + Graph._validate_adj_list(order, graph_adj_list_repr) + except GraphError as e: + raise GraphError( + "Unable to initialize Graph with given order and " + "adjacency list" + ) from e + + self.order = order + self.graph_adj_list_repr = graph_adj_list_repr + else: + self.graph_adj_list_repr = [[] for _ in range(order)] + self.order = order + + @staticmethod + def _validate_adj_list( + order: int, graph_adj_list_repr: list[list[int]] + ) -> None: + """Static method to ensure constructor arguments make sense + + Args: + order: The order of the graph + graph_adj_list_repr: A list of lists of integers comprising the + graph's adjacency list representation + + Raises: + GraphError: If the number of vertices in the adjacency list doesn't + correspond to the given graph order, if the adjacency list + is not a list of lists of integers, if any of the vertex labels + in the list of lists of integers are outside the expected + range, or if there are duplicates within a vertex's adjacency + list. + """ + if len(graph_adj_list_repr) != order: + raise GraphError( + "Number of vertices in adjacency list, " + f"{len(graph_adj_list_repr)}, doesn't correspond to " + f"desired graph order {order}." + ) + acceptable_vertex_labels = range(order) + u = -1 + for adj_list in graph_adj_list_repr: + u += 1 + adj_list_index = -1 + neighbours_seen = set() + for v in adj_list: + adj_list_index += 1 + if not isinstance(v, int): + raise GraphError( + "Adjacency list representation must be a list of " + f"lists of integers; list for vertex {u} contains " + f"{v}, which is not an integer." + ) + + if v == -1: + if len(graph_adj_list_repr[adj_list_index + 1]) > 0: + raise GraphError( + f"Adjacency list for vertex {u} is malformed: " + "there must be no elements after -1, since this " + "is used to terminate the 0-based adjacency list." + ) + elif v not in acceptable_vertex_labels: + raise GraphError( + f"Adjacency list for vertex {u} includes vertex {v}, " + "which is outside the range " + f"[{acceptable_vertex_labels[0]}, " + f"{acceptable_vertex_labels[-1]}]." + ) + + if v in neighbours_seen: + raise GraphError( + "Duplicate neighbours present in adjacency list for " + f"vertex {u}." + ) + neighbours_seen.add(v) + + def __copy__(self): + raise NotImplementedError( + "Shallow copy is not supported for Graph objects, since the " + "adjacency list is a list of a mutable type, lists of integers." + ) + + def __deepcopy__(self, memo): + """Deepcopy Graph object + The Graph class has a mutable member, graph_adj_list_repr, which is a + list of lists of integers. Therefore, it's necessary to implement + deepcopy so that when we delete_edge() from a copy of a graph, it + doesn't change the original graph's graph_adj_list_repr. + """ + if id(self) in memo: + return memo[id(self)] + + graph_deepcopy = Graph( + self.order, deepcopy(self.graph_adj_list_repr, memo) + ) + + memo[id(self)] = graph_deepcopy + + return graph_deepcopy + + def __eq__(self, other) -> bool: + """Graph equality + + Implementing the simplest form of equality from a representational + standpoint. + + There are other definitions of equality of graphs that are relevant in + other applications, but originally this was here to help debugging + __deepcopy__: after creating a deepcopy of a graph, they were equal, + but then after changing one of the adjacency lists, they were no longer + equal. + + Returns: + If you're comparing any other object to the Graph, if the orders of + two graphs don't agree, if the number of elements in the outer list + disagree (i.e. two different vertex sets), or if the corresponding + adjacency lists disagree for any of the vertices, then return False + """ + if ( + isinstance(other, Graph) + and self.order == other.order + and len(self.graph_adj_list_repr) == len(other.graph_adj_list_repr) + and all( + u_list == v_list + for (u_list, v_list) in zip( + self.graph_adj_list_repr, other.graph_adj_list_repr + ) + ) + ): + return True + return False + + def __str__(self) -> str: + """Get Graph's string representation + + Returns: + Returns str of graph's adjacency list representation that matches + what's produced by _WriteAdjList() in edge-addition-planarity-suite + e.g. + N={order} + 0: X Y Z -1 + ... + """ + adj_list_str = f"N={self.order}\n" + u = -1 + for adj_list in self.graph_adj_list_repr: + u += 1 + + line = f"{u}: " + for v in adj_list: + line += f"{v} " + line += "-1\n" + + adj_list_str += line + return adj_list_str + + def _vertex_validation(self, u: int): + """Ensure vertex has label in range for 0-based graphs + + Args: + u: Vertex index + + Raises: + GraphError: If endpoints are out of bounds for 0-based labelling + """ + if u >= self.order or u < 0: + raise GraphError( + "Invalid vertex label; must be in closed interval " + f"[0, {self.order - 1}]." + ) + + def add_arc(self, u: int, v: int): + """Add arc to graph's adjacency list representation + + Args: + u: Index of tail vertex of arc to add + v: Index head vertex of arc to add + Raises: + GraphError: If arc endpoints are invalid + """ + try: + self._vertex_validation(u) + self._vertex_validation(v) + except GraphError as e: + raise GraphError(f"Unable to add arc ({u}, {v}).") from e + + self.graph_adj_list_repr[u].append(v) + + def get_max_degree(self) -> int: + """Infer max degree from graph adjacency list representation + Returns: + The length of the longest adjacency list in the graph adjacency + list representation (i.e. the list of lists of integers) + """ + return max(len(x) for x in self.graph_adj_list_repr) + + def get_vertex_degree_counts(self) -> dict[int, int]: + """Determine number of vertices of each degree in graph + + Returns: + Dict mapping integers (degree) to integers (number of vertices of + of that degree in graph) + """ + vertex_degree_counts = {} + for adj_list in self.graph_adj_list_repr: + deg_of_u = len(adj_list) + try: + vertex_degree_counts[deg_of_u] += 1 + except KeyError: + vertex_degree_counts[deg_of_u] = 1 + + return vertex_degree_counts + + def delete_edge(self, u: int, v: int): + """Delete edge + + Removes both arcs from the respective vertex adjacency lists + + Args: + u: Index of first vertex incident on edge to delete + v: Index of second vertex incident on edge to delete + + Raises: + GraphError: If edge endpoints are invalid + """ + try: + self._vertex_validation(u) + self._vertex_validation(v) + except GraphError as e: + raise GraphError(f"Unable to delete edge {{{u}, {v}}}.") from e + + self.graph_adj_list_repr[u].remove(v) + self.graph_adj_list_repr[v].remove(u) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph_generation_orchestrator.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph_generation_orchestrator.py new file mode 100644 index 000000000..fb7fd4a93 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/graph_generation_orchestrator.py @@ -0,0 +1,209 @@ +"""Use multithreading and subprocess modules to parallelize geng calls + +Functions: + _call_geng( + geng_path: Path, + canonical_files: bool, + order: int, + num_edges: int, + output_dir: Path, + ) -> None + + _validate_and_normalize_geng_workload_args( + geng_path: Path, order: int, output_dir: Path + ) -> tuple[Path, int, Path] + + distribute_geng_workload( + geng_path: Path, canonical_files: bool, order: int, output_dir: Path + ) -> None +""" + +#!/usr/bin/env python + +__all__ = ["distribute_geng_workload"] + +import sys +import multiprocessing +import subprocess +import argparse +import shutil +from pathlib import Path + +from planaritytesting_utils import ( + max_num_edges_for_order, + is_path_to_executable, +) + + +def _call_geng( + geng_path: Path, + canonical_files: bool, + order: int, + num_edges: int, + output_dir: Path, +) -> None: + """Call nauty geng as blocking process on multiprocessing thread + + Opens a file for write (overwrites file if it exists) within the output_dir + and uses subprocess.run() to start a blocking process on the + multiprocessing pool thread to call the nauty geng executable with the + desired order and number of edges, with stdout redirected to the output + file object. + + The resulting .g6 output file will contain all graphs of the desired order + for a single edge count. + + Args: + geng_path: Path to the nauty geng executable + canonical_files: Bool to indicate whether or not to generate + graphs with canonical labelling + order: Desired number of vertices + num_edges: Desired number of edges + output_dir: Directory to which you wish to write the resulting .g6 file + """ + filename = Path.joinpath( + output_dir, + f"n{order}.m{num_edges}{'.canonical' if canonical_files else ''}.g6", + ) + with open(filename, "w", encoding="utf-8") as outfile: + command = [f"{geng_path}", f"{order}", f"{num_edges}:{num_edges}"] + if canonical_files: + command.insert(1, "-l") + subprocess.run( + command, stdout=outfile, stderr=subprocess.PIPE, check=False + ) + + +def _validate_and_normalize_geng_workload_args( + geng_path: Path, order: int, output_dir: Path +) -> tuple[Path, int, Path]: + """Validates and normalizes args provided to distribute_geng_workload + + Ensures geng_path corresponds to an executable, that order is an integer + in closed interval [2, 12], and that output_dir is a valid Path specifying + where results from executing geng should be output. + + Args: + geng_path: Path to the nauty geng executable + order: Desired number of vertices + output_dir: Directory to which you wish to write the resulting .g6 file + If none provided, defaults to: + TestSupport/results/graph_generation_orchestrator/{order} + + Raises: + argparse.ArgumentTypeError: If any of the args passed from the command + line are determined invalid under more specific scrutiny + Returns: + A tuple comprised of the geng_path, order, and output_dir + """ + if not is_path_to_executable(geng_path): + raise argparse.ArgumentTypeError( + f"Path for geng executable '{geng_path}' does not correspond to " + "an executable." + ) + + if not order or order < 2 or order > 12: + raise argparse.ArgumentTypeError( + "Graph order must be between 2 and 12." + ) + + if not output_dir: + test_support_dir = Path(sys.argv[0]).resolve().parent.parent + output_parent_dir = Path.joinpath( + test_support_dir, "results", "graph_generation_orchestrator" + ) + candidate_output_dir = Path.joinpath(output_parent_dir, f"{order}") + output_dir = candidate_output_dir + elif not isinstance(output_dir, Path) or output_dir.is_file(): + raise argparse.ArgumentTypeError("Output directory path is invalid.") + + output_dir = output_dir.resolve() + try: + candidate_order_from_path = (int)(output_dir.parts[-1]) + except ValueError: + output_dir = Path.joinpath(output_dir, str(order)) + except IndexError as e: + raise argparse.ArgumentTypeError( + f"Unable to extract parts from output dir path '{output_dir}'." + ) from e + else: + if candidate_order_from_path != order: + raise argparse.ArgumentTypeError( + f"Output directory '{output_dir}' seems to indicate " + f"graph order should be '{candidate_order_from_path}'" + f", which does not mach order from command line args " + f"'{order}'. Please verify your command line args and retry." + ) + + Path.mkdir(output_dir, parents=True, exist_ok=True) + + return geng_path, order, output_dir + + +def distribute_geng_workload( + geng_path: Path, canonical_files: bool, order: int, output_dir: Path +) -> None: + """Use starmap_async on multiprocessingn pool to _call_geng + + Args: + geng_path: Path to the nauty geng executable + order: Desired number of vertices + generate_canonical: Bool to indicate whether or not to generate + graphs with canonical labelling + output_dir: Directory to which you wish to write the resulting .g6 file + """ + geng_path, order, output_dir = _validate_and_normalize_geng_workload_args( + geng_path, order, output_dir + ) + + Path.mkdir(output_dir, parents=True, exist_ok=True) + + call_geng_args = [ + (geng_path, canonical_files, order, edge_count, output_dir) + for edge_count in range(max_num_edges_for_order(order) + 1) + ] + + with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool: + _ = pool.starmap_async(_call_geng, call_geng_args) + pool.close() + pool.join() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="Graph Generation Orchestrator\n" + "Orchestrates calls to nauty's geng to generate graphs for a " + "given order, separated out into files for each edge count. The " + "output files will have paths:\n" + "\t{output_dir}/{order}/n{order}.m{num_edges}(.canonical)?.g6", + ) + parser.add_argument( + "-g", "--gengpath", type=Path, metavar="PATH_TO_GENG_EXECUTABLE" + ) + parser.add_argument( + "-l", + "--canonicalfiles", + action="store_true", + help="Generate canonical .g6 files", + ) + parser.add_argument("-n", "--order", type=int, default=11, metavar="N") + parser.add_argument( + "-o", + "--outputdir", + type=Path, + default=None, + metavar="G6_OUTPUT_DIR", + help="If no output directory provided, defaults to\n" + "\tTestSupport/results/graph_generation_orchestrator/{order}", + ) + + args = parser.parse_args() + + distribute_geng_workload( + geng_path=args.gengpath, + canonical_files=args.canonicalfiles, + order=args.order, + output_dir=args.outputdir, + ) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config_manager.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config_manager.py new file mode 100644 index 000000000..64dd8fb7c --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config_manager.py @@ -0,0 +1,200 @@ +"""Create default config file for values required by PlanarityLeaksOrchestrator + +Functions: + initialize_planarity_leaks_orchestrator_config(config_path: Path) -> None + initialize_test_random_graphs_config(config_path: Path) -> None + initialize_test_random_max_planar_graph_generator_config( + config_path: Path, + ) -> None: + initialize_test_random_nonplanar_graph_generator_config( + config_path: Path, + ) -> None: + initialize_test_specific_graph_config(config_path: Path) -> None + initialize_test_transform_graph_config(config_path: Path) -> None + initialize_test_test_all_graphs_config(config_path: Path) -> None +""" + +#!/usr/bin/env python + +__all__ = [] + +import configparser +from pathlib import Path +import sys + + +def initialize_planarity_leaks_orchestrator_config(config_path: Path) -> None: + """Initialize planarity leaks orchestrator config file with defaults + + Args: + config_path: Path to config file to initialize with defaults + """ + initialize_test_random_graphs_config(config_path) + initialize_test_random_max_planar_graph_generator_config(config_path) + initialize_test_random_nonplanar_graph_generator_config(config_path) + initialize_test_specific_graph_config(config_path) + initialize_test_transform_graph_config(config_path) + initialize_test_test_all_graphs_config(config_path) + + +def initialize_test_random_graphs_config(config_path: Path) -> None: + """Initialize planarity leaks orchestrator RandomGraphs config + + Args: + config_path: Path to config file to initialize with defaults for + test_random_graphs() + """ + test_random_graphs_config = configparser.ConfigParser() + test_random_graphs_config.read(config_path) + + test_random_graphs_config["RandomGraphs"] = { + "enabled": "False", + "perform_full_analysis": "True", + "num_graphs": "500", + "order": "1000", + "commands_to_run": "", + } + + with open(config_path, "w", encoding="utf-8") as config_file: + test_random_graphs_config.write(config_file) + + +def initialize_test_random_max_planar_graph_generator_config( + config_path: Path, +) -> None: + """Initialize planarity leaks orchestrator callRandomMaxPlanarGraph config + + Args: + config_path: Path to config file to initialize with defaults for + PlanarityLeaksOrchestrator.test_random_max_planar_graph_generator() + """ + test_random_max_planar_graph_generator_config = configparser.ConfigParser() + test_random_max_planar_graph_generator_config.read(config_path) + + test_random_max_planar_graph_generator_config[ + "RandomMaxPlanarGraphGenerator" + ] = { + "enabled": "False", + "perform_full_analysis": "True", + "order": "1000", + } + + with open(config_path, "w", encoding="utf-8") as config_file: + test_random_max_planar_graph_generator_config.write(config_file) + + +def initialize_test_random_nonplanar_graph_generator_config( + config_path: Path, +) -> None: + """Initialize planarity leaks orchestrator callRandomNonplanarGraph config + + Args: + config_path: Path to config file to initialize with defaults for + PlanarityLeaksOrchestrator.test_random_nonplanar_graph_generator() + """ + test_random_nonplanar_graph_generator_config = configparser.ConfigParser() + test_random_nonplanar_graph_generator_config.read(config_path) + + test_random_nonplanar_graph_generator_config[ + "RandomNonplanarGraphGenerator" + ] = { + "enabled": "False", + "perform_full_analysis": "True", + "order": "1000", + } + + with open(config_path, "w", encoding="utf-8") as config_file: + test_random_nonplanar_graph_generator_config.write(config_file) + + +def initialize_test_specific_graph_config(config_path: Path) -> None: + """Initialize planarity leaks orchestrator SpecificGraph config + + Args: + config_path: Path to config file to initialize with defaults for + PlanarityLeaksOrchestrator.test_specific_graph() + """ + test_specific_graph_config = configparser.ConfigParser() + test_specific_graph_config.read(config_path) + + test_specific_graph_config["SpecificGraph"] = { + "enabled": "False", + "perform_full_analysis": "True", + "infile_path": ">>CHANGEME<<", + "commands_to_run": "", + } + + with open(config_path, "w", encoding="utf-8") as config_file: + test_specific_graph_config.write(config_file) + + +def initialize_test_transform_graph_config(config_path: Path) -> None: + """Initialize planarity leaks orchestrator Graph Transformation config + + Args: + config_path: Path to config file to initialize with defaults for + PlanarityLeaksOrchestrator.test_transform_graph() + """ + test_transform_graph_config = configparser.ConfigParser() + test_transform_graph_config.read(config_path) + + test_transform_graph_config["TransformGraph"] = { + "enabled": "False", + "perform_full_analysis": "True", + "infile_path": ">>CHANGEME<<", + "output_formats_to_test": "", + } + + with open(config_path, "w", encoding="utf-8") as config_file: + test_transform_graph_config.write(config_file) + + +def initialize_test_test_all_graphs_config(config_path: Path) -> None: + """Initialize planarity leaks orchestrator TestAllGraphs config + + Args: + config_path: Path to config file to initialize with defaults for + PlanarityLeaksOrchestrator.test_test_all_graphs() + """ + test_test_all_graphs_config = configparser.ConfigParser() + test_test_all_graphs_config.read(config_path) + + test_test_all_graphs_config["TestAllGraphs"] = { + "enabled": "False", + "perform_full_analysis": "True", + "infile_path": ">>CHANGEME<<", + "commands_to_run": "", + } + + with open(config_path, "w", encoding="utf-8") as config_file: + test_test_all_graphs_config.write(config_file) + + +if __name__ == "__main__": + config = configparser.ConfigParser( + converters={ + "list": lambda x: ( + [i.strip() for i in x.split(",") if i] if len(x) > 0 else [] + ) + } + ) + leaks_orchestrator_package_root = Path(sys.argv[0]).resolve().parent + default_config_path = Path.joinpath( + leaks_orchestrator_package_root, + "planarity_leaks_config.ini", + ) + config.read(default_config_path) + + if not config.sections(): + initialize_planarity_leaks_orchestrator_config(default_config_path) + else: + response = input( + "Do you wish to re-initialize the default config file? [Y/n]" + ) + if response.lower() == "y": + initialize_planarity_leaks_orchestrator_config(default_config_path) + else: + print( + "No changes have been made to planarity leaks default " + f"configuration file at '{default_config_path}'." + ) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_orchestrator.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_orchestrator.py new file mode 100644 index 000000000..7ced0a446 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_orchestrator.py @@ -0,0 +1,987 @@ +"""Run leaks on MacOS to test for memory issues, e.g. leaks or overruns""" + +#!/usr/bin/env python + +__all__ = [] + +import argparse +from datetime import datetime +import configparser +import multiprocessing +import os +from pathlib import Path +import platform +import shutil +import subprocess +import sys +from typing import Optional + +# This is required to import from modules in the parent package +sys.path.append(str(Path(sys.argv[0]).resolve().parent.parent)) + +# pylint: disable=wrong-import-position + +from planaritytesting_utils import ( + PLANARITY_ALGORITHM_SPECIFIERS, + GRAPH_FORMAT_SPECIFIERS, + determine_input_filetype, + is_path_to_executable, +) + +# pylint: enable=wrong-import-position + + +class PlanarityLeaksOrchestratorError(Exception): + """ + Custom exception for representing errors that arise when performing memory + checks on a planarity executable. + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class PlanarityLeaksOrchestrator: + """Driver for planarity memory leak analysis on MacOS using leaks util""" + + def __init__( + self, + planarity_path: Optional[Path] = None, + output_dir: Optional[Path] = None, + ) -> None: + """Initialize PlanarityLeaksOrchestrator instance + + Args: + planarity_path: Path to planarity executable + output_dir: Directory under which subdirectories will be created + for each of the jobs specified in planarity_leaks_config.ini + Raises: + argparse.ArgumentTypeError: If the planarity_path doesn't + correspond to an executable, if the output_dir corresponds to + a file, + OSError: If run on any platform other than MacOS, or if the leaks + utility is not found + """ + if platform.system() != "Darwin": + raise OSError("This utility is only for use on MacOS.") + + if not shutil.which("leaks"): + raise OSError( + "leaks is not installed; please install leaks and ensure your " + "path variable contains the directory to which it is " + "installed." + ) + + planarity_project_root = ( + Path(sys.argv[0]).resolve().parent.parent.parent.parent + ) + if not planarity_path: + planarity_path = Path.joinpath( + planarity_project_root, "Release", "planarity" + ) + else: + planarity_path = planarity_path.resolve() + + if not is_path_to_executable(planarity_path): + raise argparse.ArgumentTypeError( + f"'Path for planarity executable {planarity_path}' doesn't " + "correspond to an executable." + ) + + self.curr_timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + + if not output_dir: + output_dir = Path.joinpath( + planarity_project_root, + "TestSupport", + "results", + "planarity_leaks_orchestrator", + ) + + if output_dir.is_file(): + raise argparse.ArgumentTypeError( + f"Path '{output_dir}' corresponds to a file, and can't be " + "used as the output directory." + ) + + output_dir = Path.joinpath(output_dir, f"{self.curr_timestamp}") + + if Path.is_dir(output_dir): + shutil.rmtree(output_dir) + + Path.mkdir(output_dir, parents=True, exist_ok=True) + + self.planarity_path = planarity_path + self.output_dir = output_dir + + @staticmethod + def setup_leaks_environment_variables( + perform_full_analysis: bool = False, + ) -> dict[str, str]: + """Sets up environment variables to configure granularity of leaks runs + + Args: + perform_full_analysis: bool to indicate whether additional + environment variables should be set for the leaks run; if True, + sets the following additional environment variables: + + MallocStackLoggingNoCompact - This option is similar to + MallocStackLogging but makes sure that all allocations are + logged, no matter how small or how short lived the buffer + may be. + MallocScribble - If set, free sets each byte of every released + block to the value 0x55. + MallocPreScribble - If set, malloc sets each byte of a newly + allocated block to the value 0xAA. This increases the + likelihood that a program making assumptions about freshly + allocated memory fails. + MallocGuardEdges - If set, malloc adds guard pages before and + after large allocations. + MallocCheckHeapStart = - If set, specifies the number of + allocations to wait before begining periodic heap + checks every as specified by MallocCheckHeapEach. If + MallocCheckHeapStart is set but MallocCheckHeapEach is not + specified, the default check repetition is 1000. + MallocCheckHeapEach = - If set, run a consistency check on + the heap every operations. MallocCheckHeapEach is only + meaningful if MallocCheckHeapStart is also set. + """ + leaks_environment_variables = os.environ.copy() + leaks_environment_variables["MallocStackLogging"] = "1" + if perform_full_analysis: + leaks_environment_variables = dict( + leaks_environment_variables, + **{ + "MallocStackLoggingNoCompact": "1", + "MallocScribble": "1", + "MallocPreScribble": "1", + "MallocGuardEdges": "1", + "MallocCheckHeapStart": "1", + "MallocCheckHeapEach": "6", + # "MallocErrorAbort": "1", + # "MallocCheckHeapAbort": "1", + }, + ) + return leaks_environment_variables + + def _run_leaks( + self, + command_args: list[str], + leaks_outfile_basename: Path, + leaks_env: dict, + cwd: Optional[Path] = None, + ) -> None: + """Run leaks utility for given args + + Args: + command_args: List of strings comprised of executable name and + command line arguments + leaks_outfile_name: name of file to which you wish to write leaks + output + leaks_env: dictionary containing all relevant environment variables + for leaks execution (e.g. os.environ() + MallocStackLogging=1) + cwd: If not None, indicates to subprocess.run() that we wish to + change the working directory to cwd before executing the child + """ + outfile_path = leaks_outfile_basename.with_suffix( + leaks_outfile_basename.suffix + ".log" + ) + if cwd: + outfile_path = Path.joinpath(cwd, outfile_path) + + full_args = [ + # Use script utility to wrap leaks because there is some issue + # capturing *all* terminal output when you invoke leaks directly + # and redirect stdout and stderr to file + "script", + f"{outfile_path}", + "leaks", + "-atExit", + "--", + ] + command_args + subprocess.run( + full_args, + # Must default to None for stdout and stdin so that script utility + # will capture *all* leaks output from running planarity; see #59 + # for investigation + env=leaks_env, + check=False, + cwd=cwd, + ) + + @staticmethod + def _valid_commands_to_run(commands_to_run: tuple[str, ...]) -> bool: + """Ensures all algorithm command specifiers in tuple are valid + + Args: + commands_to_run: tuple of strings which we wish to ensure are valid + algorithm command specifiers + + Returns: + bool indicating whether or not the commands_to_run are valid + """ + return set(commands_to_run) <= set(PLANARITY_ALGORITHM_SPECIFIERS()) + + @staticmethod + def _valid_graph_output_formats( + output_formats_to_run: tuple[str, ...] + ) -> bool: + """Ensures all graph output format specifiers in tuple are valid + + Args: + output_formats_to_run: tuple of strings which we wish to ensure are + valid graph output format specifiers + + Returns: + bool indicating whether or not the output_formats_to_run are valid + """ + return set(output_formats_to_run) <= set(GRAPH_FORMAT_SPECIFIERS()) + + def test_random_graphs( + self, + num_graphs_to_generate: int, + order: int, + commands_to_run: tuple[str, ...] = (), + perform_full_analysis: bool = False, + ) -> None: + """Check RandomGraphs() for memory issues + 'planarity -r [-q] C K N': Random graphs + + Args: + num_graphs_to_generate: Number of graphs to randomly generate + order: Number of vertices in each randomly generated graph + commands_to_run: Tuple containing algorithm command specifiers + which we wish to use to test random graphs. + perform_full_analysis: bool to determine what environment variables + leaks_env will hold (to be sent to subprocess.run()) + + Raises: + PlanarityLeaksOrchestratorError: If commands_to_run contains [an] + invalid algorithm command specifier(s) + """ + if not commands_to_run: + commands_to_run = PLANARITY_ALGORITHM_SPECIFIERS() + elif not self._valid_commands_to_run(commands_to_run): + raise PlanarityLeaksOrchestratorError( + "commands_to_run param contains invalid command specifiers: " + f"'{commands_to_run}'." + ) + + leaks_env = self.setup_leaks_environment_variables( + perform_full_analysis + ) + + test_random_graphs_args = [ + (command, num_graphs_to_generate, order, leaks_env) + for command in commands_to_run + ] + with multiprocessing.Pool( + processes=multiprocessing.cpu_count() + ) as pool: + _ = pool.starmap_async( + self._test_random_graphs, test_random_graphs_args + ) + pool.close() + pool.join() + + def _test_random_graphs( + self, + command: str, + num_graphs_to_generate: int, + order: int, + leaks_env: dict, + ) -> None: + """Runs RandomGraphs() for a given algorithm command specifier + + Args: + command: algorithm command specifier + num_graphs_to_generate: Number of graphs to randomly generate + order: Number of vertices in each randomly generated graph + leaks_env: dictionary containing all relevant environment variables + for leaks execution (e.g. os.environ() + MallocStackLogging=1) + """ + random_graphs_args = [ + f"{self.planarity_path}", + "-r", + f"-{command}", + f"{num_graphs_to_generate}", + f"{order}", + ] + random_graphs_outfile_parent_dir = Path.joinpath( + self.output_dir, + "RandomGraphs", + ) + Path.mkdir( + random_graphs_outfile_parent_dir, parents=True, exist_ok=True + ) + random_graphs_leaks_outfile_basename = Path.joinpath( + random_graphs_outfile_parent_dir, + f"RandomGraphs.{command}.{num_graphs_to_generate}.{order}", + ) + self._run_leaks( + random_graphs_args, random_graphs_leaks_outfile_basename, leaks_env + ) + + def test_random_max_planar_graph_generator( + self, + order: int, + perform_full_analysis: bool = False, + ) -> None: + """Check callRandomMaxPlanarGraph for memory issues + + 'planarity -rm [-q] N O [O2]': Maximal planar random graph + + Args: + order: Desired number of vertices for randomly generated maximal + planar graph + perform_full_analysis: bool to determine what environment variables + leaks_env will hold (to be sent to subprocess.run()) + """ + leaks_env = ( + PlanarityLeaksOrchestrator.setup_leaks_environment_variables( + perform_full_analysis + ) + ) + max_planar_graph_generator_outfile_parent_dir = Path.joinpath( + self.output_dir, + "RandomMaxPlanarGraphGenerator", + ) + Path.mkdir( + max_planar_graph_generator_outfile_parent_dir, + parents=True, + exist_ok=True, + ) + leaks_outfile_basename = Path.joinpath( + max_planar_graph_generator_outfile_parent_dir, + f"RandomMaxPlanarGraphGenerator.{order}", + ) + adjlist_before_processing = leaks_outfile_basename.with_suffix( + leaks_outfile_basename.suffix + ".AdjList.BEFORE.out.txt" + ) + adjlist_after_processing = leaks_outfile_basename.with_suffix( + leaks_outfile_basename.suffix + ".AdjList.AFTER.out.txt" + ) + + random_max_planar_graph_args = [ + f"{self.planarity_path}", + "-rm", + # planarityRandomGraphs.c line 432 - we don't want to + # saveEdgeListFormat, since we're only testing what's available + # from command line + "-q", + f"{order}", + f"{adjlist_after_processing}", + f"{adjlist_before_processing}", + ] + self._run_leaks( + random_max_planar_graph_args, + leaks_outfile_basename, + leaks_env, + ) + + def test_random_nonplanar_graph_generator( + self, + order: int, + perform_full_analysis: bool = False, + ) -> None: + """Check callRandomNonplanarGraph for memory issues + + 'planarity -rn [-q] N O [O2]': Non-planar random graph (maximal planar + plus edge) + + Args: + order: Desired number of vertices for randomly generated nonplanar + graph + perform_full_analysis: bool to determine what environment variables + leaks_env will hold (to be sent to subprocess.run()) + """ + leaks_env = ( + PlanarityLeaksOrchestrator.setup_leaks_environment_variables( + perform_full_analysis + ) + ) + random_nonplanar_graph_generator_outfile_parent_dir = Path.joinpath( + self.output_dir, + "RandomNonplanarGraphGenerator", + ) + Path.mkdir( + random_nonplanar_graph_generator_outfile_parent_dir, + parents=True, + exist_ok=True, + ) + leaks_outfile_basename = Path.joinpath( + random_nonplanar_graph_generator_outfile_parent_dir, + f"RandomNonplanarGraphGenerator.{order}", + ) + adjlist_before_processing = leaks_outfile_basename.with_suffix( + leaks_outfile_basename.suffix + ".AdjList.BEFORE.out.txt" + ) + adjlist_after_processing = leaks_outfile_basename.with_suffix( + leaks_outfile_basename.suffix + ".AdjList.AFTER.out.txt" + ) + + random_nonplanar_graph_generator_args = [ + f"{self.planarity_path}", + "-rn", + "-q", + f"{order}", + f"{adjlist_after_processing}", + f"{adjlist_before_processing}", + ] + self._run_leaks( + random_nonplanar_graph_generator_args, + leaks_outfile_basename, + leaks_env, + ) + + def test_specific_graph( + self, + infile_path: Path, + commands_to_run: tuple[str, ...] = (), + perform_full_analysis: bool = False, + ) -> None: + """Check SpecificGraph() for memory issues for given commands + + Args: + infile_path: Path to .g6 infile containing single graph on which we + wish to run the algorithms specified in commands_to_run + commands_to_run: Tuple containing algorithm command specifiers + which we wish to use to test specific graph. + perform_full_analysis: bool to determine what environment variables + leaks_env will hold (to be sent to subprocess.run()) + + Raises: + PlanarityLeaksOrchestratorError: If input_file is not a valid .g6 + file, or if commands_to_run contains at least one invalid + algorithm command specifier. + """ + try: + file_type = determine_input_filetype(infile_path) + except ValueError as input_filetype_error: + raise PlanarityLeaksOrchestratorError( + "Failed to determine input filetype of " f"'{infile_path}'." + ) from input_filetype_error + + if file_type != "G6": + raise PlanarityLeaksOrchestratorError( + f"Determined '{infile_path}' has filetype '{file_type}', " + "which is not supported; please supply a .g6 file." + ) + if not commands_to_run: + commands_to_run = PLANARITY_ALGORITHM_SPECIFIERS() + elif not self._valid_commands_to_run(commands_to_run): + raise PlanarityLeaksOrchestratorError( + "commands_to_run param contains invalid contains at least one " + f"invalid algorithm command specifier: '{commands_to_run}'." + ) + + specific_graph_outfile_parent_dir = Path.joinpath( + self.output_dir, + "SpecificGraph", + ) + Path.mkdir( + specific_graph_outfile_parent_dir, + parents=True, + exist_ok=True, + ) + + infile_copy_path = Path.joinpath( + specific_graph_outfile_parent_dir, infile_path.name + ).resolve() + + shutil.copyfile(infile_path, infile_copy_path) + + infile_path = infile_copy_path + + leaks_env = ( + PlanarityLeaksOrchestrator.setup_leaks_environment_variables( + perform_full_analysis + ) + ) + + test_specific_graph_args = [ + ( + specific_graph_outfile_parent_dir, + infile_path, + command, + leaks_env, + ) + for command in commands_to_run + ] + with multiprocessing.Pool( + processes=multiprocessing.cpu_count() + ) as pool: + _ = pool.starmap_async( + self._test_specific_graph, test_specific_graph_args + ) + pool.close() + pool.join() + + def _test_specific_graph( + self, + specific_graph_outfile_parent_dir: Path, + infile_path: Path, + command: str, + leaks_env: dict, + ) -> None: + """Check SpecificGraph() for memory issues for given command + + 'planarity -s [-q] C I O [O2]': Specific graph + + Args: + specific_graph_outfile_parent_dir: Directory to which results for + testing SpecificGraph() will be output + infile_path: Path to .g6 infile containing single graph on which we + wish to run the algorithm specified by command + command: Algorithm command specifier to indicate what + algorithm you wish to run SpecificGraph() with + leaks_env: dictionary containing all relevant environment variables + for leaks execution (e.g. os.environ() + MallocStackLogging=1) + """ + specific_graph_leaks_outfile_basename = Path( + f"SpecificGraph.{infile_path.with_suffix('').name}.{command}", + ) + specific_graph_primary_output = ( + specific_graph_leaks_outfile_basename.with_suffix( + specific_graph_leaks_outfile_basename.suffix + + ".PRIMARY.out.txt" + ) + ) + specific_graph_secondary_output = ( + specific_graph_leaks_outfile_basename.with_suffix( + specific_graph_leaks_outfile_basename.suffix + + ".SECONDARY.out.txt" + ) + ) + specific_graph_args = [ + f"{self.planarity_path}", + "-s", + f"-{command}", + f"{infile_path.name}", + f"{specific_graph_primary_output}", + f"{specific_graph_secondary_output}", + ] + self._run_leaks( + command_args=specific_graph_args, + leaks_outfile_basename=specific_graph_leaks_outfile_basename, + leaks_env=leaks_env, + cwd=specific_graph_outfile_parent_dir, + ) + + def test_transform_graph( + self, + infile_path: Path, + output_formats_to_test: tuple[str, ...] = (), + perform_full_analysis: bool = False, + ) -> None: + """Check Graph Transformation for memory issues for given formats + + Args: + infile_path: Path to graph input file containing single graph to be + transformed to each of the desired output_formats_to_test + output_formats_to_test: Tuple containing graph output formats to + to test. + perform_full_analysis: bool to determine what environment variables + leaks_env will hold (to be sent to subprocess.run()) + + Raises: + PlanarityLeaksOrchestratorError: If input_file is not a valid graph + input file, or if output_formats_to_test contains at least one + invalid graph output format specifier. + """ + try: + _ = determine_input_filetype(infile_path) + except ValueError as input_filetype_error: + raise PlanarityLeaksOrchestratorError( + "Failed to determine input filetype of " f"'{infile_path}'." + ) from input_filetype_error + + if not output_formats_to_test: + output_formats_to_test = tuple(GRAPH_FORMAT_SPECIFIERS().keys()) + elif not self._valid_graph_output_formats(output_formats_to_test): + raise PlanarityLeaksOrchestratorError( + "output_formats_to_test param contains at least one invalid " + f"graph output format specifier: '{output_formats_to_test}'." + ) + + transform_graph_outfile_parent_dir = Path.joinpath( + self.output_dir, + "TransformGraph", + ) + Path.mkdir( + transform_graph_outfile_parent_dir, + parents=True, + exist_ok=True, + ) + + infile_copy_path = Path.joinpath( + transform_graph_outfile_parent_dir, infile_path.name + ).resolve() + + shutil.copyfile(infile_path, infile_copy_path) + + infile_path = infile_copy_path + + leaks_env = self.setup_leaks_environment_variables( + perform_full_analysis + ) + + test_transform_graph_args = [ + ( + transform_graph_outfile_parent_dir, + infile_path, + output_format, + leaks_env, + ) + for output_format in output_formats_to_test + ] + with multiprocessing.Pool( + processes=multiprocessing.cpu_count() + ) as pool: + _ = pool.starmap_async( + self._test_transform_graph, test_transform_graph_args + ) + pool.close() + pool.join() + + def _test_transform_graph( + self, + transform_graph_outfile_parent_dir: Path, + infile_path: Path, + output_format: str, + leaks_env: dict, + ) -> None: + """Check Graph Transformation for memory issues for given output format + + 'planarity -t [-q] -t(gam) I O ': Transform graph + + Args: + transform_graph_outfile_parent_dir: Directory to which results for + testing graph transformation to desired format will be output + infile_path: Path to graph input file containing single graph to be + transformed to the desired output_format + output_format: Graph output format specifier to indicate the + target graph output format for which you wish to test graph + transform + leaks_env: dictionary containing all relevant environment variables + for leaks execution (e.g. os.environ() + MallocStackLogging=1) + """ + graph_format_specifiers = GRAPH_FORMAT_SPECIFIERS() + transform_graph_leaks_outfile_basename = Path( + f"{infile_path.with_suffix('').name}" + f".{graph_format_specifiers[output_format]}", + ) + transformed_graph_output = ( + transform_graph_leaks_outfile_basename.with_suffix( + transform_graph_leaks_outfile_basename.suffix + ".out.txt" + ) + ) + transform_graph_leaks_outfile_basename = Path( + "TransformGraph" + f".{transform_graph_leaks_outfile_basename}" + ) + specific_graph_args = [ + f"{self.planarity_path}", + "-t", + f"-t{output_format}", + f"{infile_path.name}", + f"{transformed_graph_output}", + ] + self._run_leaks( + command_args=specific_graph_args, + leaks_outfile_basename=transform_graph_leaks_outfile_basename, + leaks_env=leaks_env, + cwd=transform_graph_outfile_parent_dir, + ) + + def test_test_all_graphs( + self, + infile_path: Path, + commands_to_run: tuple[str, ...] = (), + perform_full_analysis: bool = False, + ) -> None: + """Check Test All Graphs for memory issues for given commands + + Args: + infile_path: Path to .g6 infile containing at least one graph on + which we wish to run the algorithms specified in + commands_to_run + commands_to_run: Tuple containing algorithm command specifiers + which we wish to use to test test all graphs. + perform_full_analysis: bool to determine what environment variables + leaks_env will hold (to be sent to subprocess.run()) + + Raises: + PlanarityLeaksOrchestratorError: If input_file is not a valid .g6 + file, or if commands_to_run contains at least one invalid + algorithm command specifier. + """ + try: + file_type = determine_input_filetype(infile_path) + except ValueError as input_filetype_error: + raise PlanarityLeaksOrchestratorError( + "Failed to determine input filetype of " f"'{infile_path}'." + ) from input_filetype_error + + if file_type != "G6": + raise PlanarityLeaksOrchestratorError( + f"Determined '{infile_path}' has filetype '{file_type}', " + "which is not supported; please supply a .g6 file." + ) + if not commands_to_run: + commands_to_run = PLANARITY_ALGORITHM_SPECIFIERS() + elif not self._valid_commands_to_run(commands_to_run): + raise PlanarityLeaksOrchestratorError( + "commands_to_run param contains invalid contains at least one " + f"invalid algorithm command specifier: '{commands_to_run}'." + ) + + test_all_graphs_outfile_parent_dir = Path.joinpath( + self.output_dir, + "TestAllGraphs", + ) + Path.mkdir( + test_all_graphs_outfile_parent_dir, + parents=True, + exist_ok=True, + ) + + infile_copy_path = Path.joinpath( + test_all_graphs_outfile_parent_dir, infile_path.name + ).resolve() + + shutil.copyfile(infile_path, infile_copy_path) + + infile_path = infile_copy_path + + leaks_env = ( + PlanarityLeaksOrchestrator.setup_leaks_environment_variables( + perform_full_analysis + ) + ) + + test_specific_graph_args = [ + ( + test_all_graphs_outfile_parent_dir, + infile_path, + command, + leaks_env, + ) + for command in commands_to_run + ] + with multiprocessing.Pool( + processes=multiprocessing.cpu_count() + ) as pool: + _ = pool.starmap_async( + self._test_test_all_graphs, test_specific_graph_args + ) + pool.close() + pool.join() + + def _test_test_all_graphs( + self, + test_all_graphs_outfile_parent_dir: Path, + infile_path: Path, + command: str, + leaks_env: dict, + ) -> None: + """Check Test All Graphs for memory issues for given command + + 'planarity -t [-q] C I O': Test All Graphs + + Args: + test_all_graphs_outfile_parent_dir: Directory to which results for + testing Test All Graphs functionality will be output + infile_path: Path to .g6 infile containing single graph on which we + wish to run the algorithm specified by command + command: Algorithm command specifier to indicate what + algorithm you wish to run Test All Graphs with + leaks_env: dictionary containing all relevant environment variables + for leaks execution (e.g. os.environ() + MallocStackLogging=1) + """ + test_all_graphs_leaks_outfile_basename = Path( + f"TestAllGraphs.{infile_path.with_suffix('').name}.{command}", + ) + test_all_graphs_output = ( + test_all_graphs_leaks_outfile_basename.with_suffix( + test_all_graphs_leaks_outfile_basename.suffix + ".out.txt" + ) + ) + + test_all_graphs_args = [ + f"{self.planarity_path}", + "-t", + f"-{command}", + f"{infile_path.name}", + f"{test_all_graphs_output}", + ] + self._run_leaks( + command_args=test_all_graphs_args, + leaks_outfile_basename=test_all_graphs_leaks_outfile_basename, + leaks_env=leaks_env, + cwd=test_all_graphs_outfile_parent_dir, + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options] > /dev/null 2>&1", + description="Planarity leaks analysis\n" + "Automates runs of leaks on MacOS to identify memory issues for the " + "following:\n" + "- Random graphs with one small graph,\n" + "- Random max planar graph generator,\n" + "- Random nonplanar graph generator,\n" + "- Specific Graph (will only run on first graph in input file)\n" + "- Graph Transformation tool\n" + "- Test All Graphs\n" + "\n\tN.B. One must run the planarity_leaks_config_manager.py script, " + "and update default configuration values. For example, you must " + "provide an infile_name for the SpecificGraph job, and any jobs you " + "wish to run must have 'enabled' set to True.\n\n" + "If an output directory is specified, a subdirectory will be created " + "to contain the results:\n" + "\t{output_dir}/{test_timestamp}/\n" + "If an output directory is not specified, defaults to:\n" + "\tTestSupport/results/planarity_leaks_orchestrator/" + "{test_timestamp}/\n", + ) + parser.add_argument( + "-p", + "--planaritypath", + type=Path, + default=None, + help="Must be a path to a planarity executable that was compiled " + "with -g; defaults to:\n" + "\t{planarity_project_root}/Release/planarity", + metavar="PATH_TO_PLANARITY_EXECUTABLE", + ) + parser.add_argument( + "-c", + "--configfile", + type=Path, + default=None, + metavar="PATH_TO_CONFIG_FILE", + help="Optional path to planarity leaks orchestrator config file;" + "defaults to:\n" + "\t{planarity_project_root}/TestSupport/planaritytesting/" + "leaksorchestrator/planarity_leaks_config.ini", + ) + parser.add_argument( + "-o", + "--outputdir", + type=Path, + default=None, + metavar="DIR_FOR_RESULTS", + help="If no output directory provided, defaults to\n" + "\t{planarity_project_root}/TestSupport/results/" + "planarity_leaks_orchestrator/", + ) + + args = parser.parse_args() + + leaks_orchestrator_root = Path(sys.argv[0]).resolve().parent + config_file = args.configfile + if not config_file: + config_file = Path.joinpath( + leaks_orchestrator_root, + "planarity_leaks_config.ini", + ) + config = configparser.ConfigParser( + converters={ + "list": lambda x: ( + [i.strip() for i in x.split(",") if i] if len(x) > 0 else [] + ) + } + ) + config.read(config_file) + if not config.sections(): + raise argparse.ArgumentTypeError( + f"'{config_file}' doesn't correspond to a planarity leaks config " + "file. Please run planarity leaks config manager to generate " + "default config and modify values as necessary for the jobs you " + "wish to run." + ) + + planarity_leaks_orchestrator = PlanarityLeaksOrchestrator( + planarity_path=args.planaritypath, + output_dir=args.outputdir, + ) + for section in config: + if not config[section].getboolean("enabled"): + continue + + if section == "RandomGraphs": + num_graphs_from_config = int(config[section]["num_graphs"]) + order_from_config = int(config[section]["order"]) + commands_to_run_from_config = config.getlist( # type: ignore + section, "commands_to_run" + ) + perform_full_analysis_from_config = config[section].getboolean( + "perform_full_analysis" + ) + + planarity_leaks_orchestrator.test_random_graphs( + num_graphs_to_generate=num_graphs_from_config, + order=order_from_config, + commands_to_run=commands_to_run_from_config, + perform_full_analysis=perform_full_analysis_from_config, + ) + elif section == "RandomMaxPlanarGraphGenerator": + order_from_config = int(config[section]["order"]) + perform_full_analysis_from_config = config[section].getboolean( + "perform_full_analysis" + ) + planarity_leaks_orchestrator.test_random_max_planar_graph_generator( + order=order_from_config, + perform_full_analysis=perform_full_analysis_from_config, + ) + elif section == "RandomNonplanarGraphGenerator": + order_from_config = int(config[section]["order"]) + perform_full_analysis_from_config = config[section].getboolean( + "perform_full_analysis" + ) + planarity_leaks_orchestrator.test_random_nonplanar_graph_generator( + order=order_from_config, + perform_full_analysis=perform_full_analysis_from_config, + ) + elif section == "SpecificGraph": + infile_path_from_config = Path(config[section]["infile_path"]) + perform_full_analysis_from_config = config[section].getboolean( + "perform_full_analysis" + ) + commands_to_run_from_config = config.getlist( # type: ignore + section, "commands_to_run" + ) + planarity_leaks_orchestrator.test_specific_graph( + infile_path=infile_path_from_config, + commands_to_run=commands_to_run_from_config, + perform_full_analysis=perform_full_analysis_from_config, + ) + elif section == "TransformGraph": + infile_path_from_config = Path(config[section]["infile_path"]) + perform_full_analysis_from_config = config[section].getboolean( + "perform_full_analysis" + ) + output_formats_to_test_from_config = config.getlist( # type: ignore + section, "output_formats_to_test" + ) + planarity_leaks_orchestrator.test_transform_graph( + infile_path=infile_path_from_config, + output_formats_to_test=output_formats_to_test_from_config, + perform_full_analysis=perform_full_analysis_from_config, + ) + elif section == "TestAllGraphs": + infile_path_from_config = Path(config[section]["infile_path"]) + perform_full_analysis_from_config = config[section].getboolean( + "perform_full_analysis" + ) + commands_to_run_from_config = config.getlist( # type: ignore + section, "commands_to_run" + ) + planarity_leaks_orchestrator.test_test_all_graphs( + infile_path=infile_path_from_config, + commands_to_run=commands_to_run_from_config, + perform_full_analysis=perform_full_analysis_from_config, + ) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_orchestrator.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_orchestrator.py new file mode 100644 index 000000000..ce062d7cd --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_orchestrator.py @@ -0,0 +1,333 @@ +"""Orchestrate calls to planarity's testAllGraphs() functionality + +Functions: + call_planarity_testAllGraphs( + planarity_path: Path, + canonical_files: bool, + makeg_g6: bool, + command: str, + order: int, + num_edges: int, + input_dir: Path, + output_dir: Path, + ) -> None + + _validate_and_normalize_planarity_testAllGraphs_workload_args( + planarity_path: Path, order: int, input_dir: Path, output_dir: Path + ) -> tuple[Path, int, Path, Path] + + distribute_planarity_testAllGraphs_workload( + planarity_path: Path, + canonical_files: bool, + makeg_g6: bool, + order: int, + input_dir: Path, + output_dir: Path, + ) -> None +""" + +#!/usr/bin/env python + +__all__ = ["distribute_planarity_testAllGraphs_workload"] + +import sys +import multiprocessing +import subprocess +import argparse +from pathlib import Path + +from planaritytesting_utils import ( + PLANARITY_ALGORITHM_SPECIFIERS, + max_num_edges_for_order, + is_path_to_executable, +) + + +def call_planarity_testAllGraphs( + planarity_path: Path, + canonical_files: bool, + makeg_g6: bool, + command: str, + order: int, + num_edges: int, + input_dir: Path, + output_dir: Path, +) -> None: + """Call planarity as blocking process on multiprocessing thread + + Uses subprocess.run() to start a blocking process on the multiprocessing + pool thread to call the planarity executable so that it applies the + algorithm specified by the command to all graphs in the input file and + outputs results to the output file, i.e. calls + planarity -t {command} {infile_path} {outfile_path} + + Args: + planarity_path: Path to the planarity executable + canonical_files: Bool to indicate whether or not the .g6 input files + are in canonical form + makeg_g6: Bool to indicate whether or not the .g6 input files were + generated by planarity-backup's embedded makeg + command: Algorithm specifier character + order: Desired number of vertices + num_edges: Desired number of edges + input_dir: Directory containing the .g6 file with all graphs of the + given order and num_edges + output_dir: Directory to which you wish to write the output of applying + the algorithm corresponding to the command to all graphs in the + input .g6 file + """ + canonical_ext = ".canonical" if canonical_files else "" + makeg_ext = ".makeg" if makeg_g6 else "" + infile_path = Path.joinpath( + input_dir, f"n{order}.m{num_edges}{makeg_ext}{canonical_ext}.g6" + ) + outfile_path = Path.joinpath( + output_dir, + f"{command}", + f"n{order}.m{num_edges}{makeg_ext}{canonical_ext}.{command}.out.txt", + ) + subprocess.run( + [ + f"{planarity_path}", + "-t", + f"-{command}", + f"{infile_path}", + f"{outfile_path}", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) + + +def _validate_and_normalize_planarity_testAllGraphs_workload_args( # pylint: disable=too-many-branches + planarity_path: Path, order: int, input_dir: Path, output_dir: Path +) -> tuple[Path, int, Path, Path]: + """Validates and normalizes args provided to distribute_planarity_workload + + Ensures planarity_path corresponds to an executable, that order is an + integer in closed interval [2, 12], that input_dir is a valid Path that + corresponds to a directory containing .g6 files, and that output_dir is a + valid Path specifying where results from executing planarity should be + output. + + Args: + planarity_path: Path to the planarity executable + order: Desired number of vertices + input_dir: Directory containing all graphs of the desired order, + with each file containing all graphs of a specific edge-count. + If none provided, defaults to: + TestSupport/results/graph_generation_orchestrator/{order} + output_dir: Directory to which the results of executing planarity Test + All Graphs for the respective command on each .g6 file will be + written. If none provided, defaults to: + TestSupport/results/planarity_testAllGraphs_orchestrator/{order} + + Raises: + argparse.ArgumentTypeError: If any of the args passed from the command + line are determined invalid under more specific scrutiny + + Returns: + A tuple comprised of the planarity_path, order, input_dir, and + output_dir + """ + if not is_path_to_executable(planarity_path): + raise argparse.ArgumentTypeError( + f"Path for planarity executable '{planarity_path}' does not " + "correspond to an executable." + ) + + if not order or not isinstance(order, int) or order < 2 or order > 12: + raise argparse.ArgumentTypeError( + "Graph order must be an integer between 2 and 12." + ) + + if not input_dir: + test_support_dir = Path(sys.argv[0]).resolve().parent.parent + input_dir = Path.joinpath( + test_support_dir, + "results", + "graph_generation_orchestrator", + f"{order}", + ) + + if not isinstance(input_dir, Path) or not input_dir.is_dir(): + raise argparse.ArgumentTypeError("Input directory path is invalid.") + + if input_dir.is_dir() and not any(Path(input_dir).iterdir()): + raise argparse.ArgumentTypeError("Input dir exists, but is empty.") + + input_dir = input_dir.resolve() + try: + candidate_order_from_path = (int)(input_dir.parts[-1]) + except ValueError: + pass + except IndexError as e: + raise argparse.ArgumentTypeError( + f"Unable to extract parts from input dir path '{input_dir}'." + ) from e + else: + if candidate_order_from_path != order: + raise argparse.ArgumentTypeError( + f"Input directory '{input_dir}' seems to indicate " + f"graph order should be '{candidate_order_from_path}'" + f", which does not mach order from command line args " + f"'{order}'. Please verify your command line args and retry." + ) + + if not output_dir: + test_support_dir = Path(sys.argv[0]).resolve().parent.parent + output_parent_dir = Path.joinpath( + test_support_dir, "results", "planarity_testAllGraphs_orchestrator" + ) + candidate_output_dir = Path.joinpath(output_parent_dir, f"{order}") + output_dir = candidate_output_dir + elif not isinstance(output_dir, Path) or output_dir.is_file(): + raise argparse.ArgumentTypeError("Output directory path is invalid.") + + output_dir = output_dir.resolve() + try: + candidate_order_from_path = (int)(output_dir.parts[-1]) + except ValueError: + output_dir = Path.joinpath(output_dir, str(order)) + except IndexError as e: + raise argparse.ArgumentTypeError( + f"Unable to extract parts from output dir path '{output_dir}'." + ) from e + else: + if candidate_order_from_path != order: + raise argparse.ArgumentTypeError( + f"Output directory '{output_dir}' seems to indicate " + f"graph order should be '{candidate_order_from_path}'" + f", which does not mach order from command line args " + f"'{order}'. Please verify your command line args and retry." + ) + + Path.mkdir(output_dir, parents=True, exist_ok=True) + + return planarity_path, order, input_dir, output_dir + + +def distribute_planarity_testAllGraphs_workload( # pylint: disable=too-many-arguments + planarity_path: Path, + canonical_files: bool, + makeg_g6: bool, + order: int, + input_dir: Path, + output_dir: Path, +) -> None: + """Use starmap_async on multiprocessing pool to _call_planarity + + Args: + planarity_path: Path to the planarity executable + canonical_files: Bool to indicate whether or not the .g6 input files + are in canonical form + makeg_g6: Bool to indicate whether or not the .g6 input files were + generated by planarity-backup's embedded makeg + order: Desired number of vertices + input_dir: Directory containing all graphs of the desired order, + with each file containing all graphs of a specific edge-count + output_dir: Directory in which we will create one subdirectory per + graph algorithm command, where the results of executing planarity + Test All Graphs for the respective command on each .g6 file will be + written + """ + planarity_path, order, input_dir, output_dir = ( + _validate_and_normalize_planarity_testAllGraphs_workload_args( + planarity_path, order, input_dir, output_dir + ) + ) + + for command in PLANARITY_ALGORITHM_SPECIFIERS(): + path_to_make = Path.joinpath(output_dir, f"{command}") + Path.mkdir(path_to_make, parents=True, exist_ok=True) + + call_planarity_args = [ + ( + planarity_path, + canonical_files, + makeg_g6, + command, + order, + num_edges, + input_dir, + output_dir, + ) + for num_edges in range(max_num_edges_for_order(order) + 1) + for command in PLANARITY_ALGORITHM_SPECIFIERS() + ] + + with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool: + _ = pool.starmap_async( + call_planarity_testAllGraphs, call_planarity_args + ) + pool.close() + pool.join() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="Planarity testAllGraphs execution orchestrator\n\n" + "Orchestrates calls to planarity's Test All Graphs " + "functionality.\n" + "Expects input directory to contain a subdirectory whose name is " + "the order containing .g6 files to be tested. Each .g6 file " + "contains all graphs of the given order with a specific number of " + "edges:\n" + "\t{input_dir}/{order}/n{order}.m{num_edges}(.makeg)?" + "(.canonical)?.g6\n\n" + "Output files will have paths:\n" + "\t{output_dir}/{order}/{command}/n{order}.m{num_edges}(.makeg)?" + "(.canonical)?.{command}.out.txt", + ) + parser.add_argument( + "-p", + "--planaritypath", + type=Path, + metavar="PATH_TO_PLANARITY_EXECUTABLE", + ) + parser.add_argument( + "-l", + "--canonicalfiles", + action="store_true", + help="Indicates .g6 input files are in canonical form", + ) + parser.add_argument( + "-m", + "--makegfiles", + action="store_true", + help="Indicates .g6 input files were generated by makeg", + ) + parser.add_argument("-n", "--order", type=int, metavar="N", default=11) + parser.add_argument( + "-i", + "--inputdir", + type=Path, + default=None, + metavar="DIR_CONTAINING_G6_FILES", + help="If no input directory provided, defaults to\n" + "\tTestSupport/results/graph_generation_orchestrator/{order}", + ) + parser.add_argument( + "-o", + "--outputdir", + type=Path, + default=None, + metavar="DIR_FOR_RESULTS", + help="If no output directory provided, defaults to\n" + "\tTestSupport/results/planarity_testAllGraphs_orchestrator/" + "{order}", + ) + + args = parser.parse_args() + + distribute_planarity_testAllGraphs_workload( + planarity_path=args.planaritypath, + canonical_files=args.canonicalfiles, + makeg_g6=args.makegfiles, + order=args.order, + input_dir=args.inputdir, + output_dir=args.outputdir, + ) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_output_parsing.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_output_parsing.py new file mode 100644 index 000000000..5a72a77e0 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planarity_testAllGraphs_output_parsing.py @@ -0,0 +1,261 @@ +"""Pure Python module for processing output from planarity testAllGraphs() + +Classes: + TestAllGraphsPathError + TestAllGraphsOutputFileContentsError + +Functions: + validate_infile_name( + infile_path:Path, order: Optional[int] = 0, + command: Optional[str] = '' + ) -> tuple[int, int, str] + process_file_contents( + infile_path:Path, command: str + ) -> tuple[str, float, int, int, int, str] +""" + +__all__ = [ + "TestAllGraphsOutputFileContentsError", + "TestAllGraphsPathError", + "validate_infile_name", + "process_file_contents", +] + +from pathlib import Path +import re +from typing import Optional + +from planaritytesting_utils import ( + PLANARITY_ALGORITHM_SPECIFIERS, + max_num_edges_for_order, +) + + +class TestAllGraphsPathError(BaseException): + """ + Custom exception signalling issues with the paths of files that purportedly + correspond to Test All Graphs output files. + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class TestAllGraphsOutputFileContentsError(BaseException): + """ + Custom exception for representing errors that arise when processing + files which purportedly contain the output produced by running the + planarity Test All Graphs functionality for a single algorithm command + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +def validate_infile_name( + infile_path: Path, order: Optional[int] = 0, command: Optional[str] = "" +) -> tuple[int, int, str]: + """Checks that infile_path corresponds to output of running planarity + + Args: + infile_path: pathlib.Path object indicating the input file whose + name should be validated before processing + order: If other files have been processed previously, this value is + used for cross-validation (i.e. new infile should contain results + for graphs of the same order as was previously incorporated into + the processed_data); defaults to 0. + command: If other files have been processed previously, this value is + used for cross-validation (i.e. new infile should contain results + for running the same command on other .g6 files that were + previously incorporated into the processed_data); defaults to the + empty string. + + Returns: + order_from_filename, num_edges_from_filename, command_from_filename + + Raises: + TestAllGraphsPathError: If infile_name doesn't match the expected + pattern for an output file from planarity Test All Graphs, + if the graph order indicated by the infile_name doesn't match + previously processed files, if the num edges in the input graph + doesn't make sense (greater than max_num_edges), if the + algorithm command specifier isn't one of the supported values, + or if the algorithm command specifier doesn't match previously + processed files. + """ + infile_name = infile_path.parts[-1] + match = re.match( + r"n(?P\d+)\.m(?P\d+)(?:\.makeg)?(?:\.canonical)?" + r"(?:\.g6)?\.(?P[pdo234])\.out\.txt", + infile_name, + ) + if not match: + raise TestAllGraphsPathError( + f"Infile name '{infile_name}' doesn't match pattern." + ) + + order_from_filename = int(match.group("order")) + num_edges_from_filename = int(match.group("num_edges")) + command_from_filename = match.group("command") + + if order and order != order_from_filename: + raise TestAllGraphsPathError( + f"Infile name '{infile_name}' indicates graph order doesn't" + " equal previously derived order." + ) + + if order and num_edges_from_filename > max_num_edges_for_order(order): + raise TestAllGraphsPathError( + f"Infile name '{infile_name}' indicates graph num_edges is" + " greater than possible for a simple graph." + ) + + if command_from_filename not in PLANARITY_ALGORITHM_SPECIFIERS(): + raise TestAllGraphsPathError( + f"Infile name '{infile_name}' contains invalid algorithm " + f"command, '{command_from_filename}'." + ) + + if command and command != command_from_filename: + raise TestAllGraphsPathError( + f"Command specified in input filename, '{command_from_filename}', " + f"doesn't match previously derived algorithm command, '{command}'." + ) + + return order_from_filename, num_edges_from_filename, command_from_filename + + +def process_file_contents( + infile_path: Path, command: str +) -> tuple[str, float, int, int, int, str]: + """Processes and validates input file contents + + Uses re.match() to determine whether the file contents are of the + expected form and attempts to extract the values produced by running + planarity Test All Graphs for a given algorithm command on a specific + .g6 file containing all graphs of a given order and single edge-count. + + Args: + infile_path: pathlib.Path object indicating the input file whose + contents are validated and processed + command: expected algorithm command specifier derived from planarity + Test All Graphs output filename + + Returns: + planarity_infile_name: extracted from infile_path.parts + duration: How long it took to run the chosen graph algorithm on all + graphs of the given order for the given number of edges + numGraphs: total number of graphs processed in the .g6 infile + numOK: number of graphs for which running the planarity algorithm + specified by the command returned OK (i.e. gp_Embed() with + embedFlags corresponding to the command returned OK and + gp_TestEmbedResultIntegrity() also returned OK) + numNONEMBEDDABLE: number of graphs for which running the planarity + algorithm specified by the command returned NONEMBEDDABLE (i.e. + gp_Embed() with embedFlags corresponding to the command + returned NONEMBEDDABLE and gp_TestEmbedResultIntegrity() also + returned NONEMBEDDABLE) + errorFlag: either SUCCESS (if all graphs reported OK or + NONEMBEDDABLE) or ERROR (if an error was encountered allocating + memory for or managing the graph datastructures, if an error + was raised by the G6ReadIterator, or if the Result from + gp_Embed() doesn't concur with gp_TestEmbedResultIntegrity()) + + Raises: + TestAllGraphsOutputFileContentsError: If the input file's header + doesn't have the expected format or values for those fields, if + the body of the input file doesn't have the expected format, if + the command derived doesn't match the expected algorithm + command specifier, or if the error flag is something other than + ERROR or SUCCESS. + """ + with open(infile_path, "r", encoding="utf-8") as infile: + line = infile.readline() + match = re.match( + r'FILENAME="(?Pn\d+\.m\d+(\.makeg)?(\.canonical)?\.g6)"' + r' DURATION="(?P\d+\.\d{3})"', + line, + ) + if not match: + raise TestAllGraphsOutputFileContentsError( + f"Invalid file header in '{infile_path}'." + ) + + planarity_infile_name_from_file = match.group("filename") + if not planarity_infile_name_from_file: + raise TestAllGraphsOutputFileContentsError( + "Header doesn't contain input filename." + ) + + duration_from_file = match.group("duration") + if not duration_from_file: + raise TestAllGraphsOutputFileContentsError( + "Unable to extract duration from input file." + ) + + duration_from_file = float(duration_from_file) + + line = infile.readline() + match = re.match( + r"-(?P\w) (?P\d+) " + r"(?P\d+) (?P\d+) " + r"(?PSUCCESS|ERROR)", + line, + ) + if not match: + raise TestAllGraphsOutputFileContentsError( + "Invalid file contents." + ) + + command_from_file = match.group("command") + if command != command_from_file: + raise TestAllGraphsOutputFileContentsError( + f"Command specified in input file, '{command_from_file}', " + "doesn't match command " + f"given in input filename, '{command}'." + ) + + if command_from_file not in PLANARITY_ALGORITHM_SPECIFIERS(): + raise TestAllGraphsOutputFileContentsError( + f"Command specifier '{command_from_file}' in input file " + f"'{infile_path}' is not valid" + ) + + numGraphs_from_file = match.group("numGraphs") + if not numGraphs_from_file: + raise TestAllGraphsOutputFileContentsError( + f"Unable ot extract numGraphs from input file '{infile_path}'." + ) + + numOK_from_file = match.group("numOK") + if not numOK_from_file: + raise TestAllGraphsOutputFileContentsError( + f"Unable ot extract numOK from input file '{infile_path}'." + ) + + numNONEMBEDDABLE_from_file = match.group("numNONEMBEDDABLE") + if not numOK_from_file: + raise TestAllGraphsOutputFileContentsError( + "Unable ot extract numNONEMBEDDABLE from input file " + f"'{infile_path}'." + ) + + errorFlag_from_file = match.group("errorFlag") + if not errorFlag_from_file or errorFlag_from_file not in ( + "SUCCESS", + "ERROR", + ): + raise TestAllGraphsOutputFileContentsError( + "Invalid errorFlag; must be SUCCESS or ERROR" + ) + + return ( + planarity_infile_name_from_file, + duration_from_file, + numGraphs_from_file, + numOK_from_file, + numNONEMBEDDABLE_from_file, + errorFlag_from_file, + ) # type: ignore diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planaritytesting_utils.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planaritytesting_utils.py new file mode 100644 index 000000000..29bd63606 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/planaritytesting_utils.py @@ -0,0 +1,185 @@ +"""Shared utilities for planarity testing + +Functions: + PLANARITY_ALGORITHM_SPECIFIERS() -> tuple[str, ...] + EDGE_DELETION_ANALYSIS_SPECIFIERS() -> tuple[str, ...] + GRAPH_FORMAT_SPECIFIERS() -> dict[str, str] + max_num_edges_for_order(order: int) -> int + g6_header() -> str + g6_suffix() -> str + LEDA_header() -> str + determine_input_filetype(infile_path: Path) -> str + is_path_to_executable(executable_path: Path) -> bool + parse_range(value: str) -> tuple[int, ...] + raise_on_duplicates(ordered_pairs: list[tuple[Any, Any]]) -> Optional[Any] + +""" + +__all__ = [ + "PLANARITY_ALGORITHM_SPECIFIERS", + "EDGE_DELETION_ANALYSIS_SPECIFIERS", + "GRAPH_FORMAT_SPECIFIERS", + "max_num_edges_for_order", + "g6_header", + "g6_suffix", + "LEDA_header", + "determine_input_filetype", + "is_path_to_executable", + "parse_range", + "raise_on_duplicates", +] + +import re +from pathlib import Path +import shutil +from typing import Any, Optional + + +def PLANARITY_ALGORITHM_SPECIFIERS() -> tuple[str, ...]: + """Returns immutable tuple containing algorithm command specifiers""" + return ("p", "d", "o", "2", "3", "4") + + +def EDGE_DELETION_ANALYSIS_SPECIFIERS() -> tuple[str, ...]: + """Allowed algorithm command specifiers for edge-deletion analysis""" + return ("2", "3", "4") + + +def GRAPH_FORMAT_SPECIFIERS() -> dict[str, str]: + """Returns dict containing graph format specifiers mapped to extensions""" + return {"g": "G6", "a": "AdjList", "m": "AdjMat"} + + +def max_num_edges_for_order(order: int) -> int: + """Returns max number of edges possible for given graph order""" + return (int)((order * (order - 1)) / 2) + + +def g6_header() -> str: + """Returns expected .g6 file header string""" + return ">>graph6<<" + + +def g6_suffix() -> str: + """Returns expected .g6 file suffix""" + return ".g6" + + +def LEDA_header() -> str: + """Returns expected LEDA file header string""" + return "LEDA.GRAPH" + + +def determine_input_filetype(infile_path: Path) -> str: + """Determine input filetype + + Args: + infile_path: Path to graph input file + + Returns: + str: One of 'LEDA', 'AdjList', 'AdjMat', or 'G6' + + Raises: + ValueError: If infile doesn't exist, if it is empty, or if unable to + determine the input file encoding + """ + if not infile_path.is_file(): + raise ValueError(f"'{infile_path}' doesn't exist.") + + with open(infile_path, "r", encoding="utf-8") as infile: + first_line = infile.readline() + if not first_line: + raise ValueError(f"'{infile_path}' is empty.") + if LEDA_header() in first_line: + return "LEDA" + if re.match(r"N=(\d+)", first_line): + return "AdjList" + if first_line[0].isdigit(): + return "AdjMat" + if infile_path.suffix == g6_suffix() and ( + (first_line.find(g6_header()) == 0) + or (ord(first_line[0]) >= 63 and ord(first_line[0]) <= 126) + ): + return "G6" + + raise ValueError(f"Unable to determine filetype of '{infile_path}'.") + + +def is_path_to_executable(executable_path: Path) -> bool: + """Determine if Path corresponds to an executable + + Args: + executable_path: Path for which we wish to determine whether it + corresponds to an executable + + Returns: + bool indicating whether (True) or not (False) the executable_path + corresponds to an executable. + """ + if ( + not executable_path + or not isinstance(executable_path, Path) + or not shutil.which(str(executable_path.resolve())) + ): + return False + return True + + +def parse_range(value: str) -> tuple[int, ...]: + """Parse a single integer or a range of integers. + + Args: + value: A string of the form 'X[,Y]', i.e. either a single value for the + desired order X, or an interval inclusive of the endpoints [X, Y] + + Returns: + A tuple containing either a single element, X, or every integer from X + up to and including Y + + Raises: + ValueError: if input string does not correspond to range notation, or + if range endpoints (or singleton) aren't integers + """ + separator = "," + if separator in value: + if value.count(separator) > 1: + raise ValueError( + f"Invalid range '{value}' contains multiple commas; range " + "should be of the form 'X,Y'" + ) + + start, end = value.split(separator) + try: + start, end = int(start), int(end) + except ValueError as int_cast_error: + raise ValueError( + f"Invalid range '{value}': both start and end values must be " + "integers." + ) from int_cast_error + + if start > end: + raise ValueError( + f"Invalid range '{value}': start value must not be greater " + "than end value." + ) + # Transforms to interval that includes endpoints: '5,8' corresponds to + # the tuple (5, 6, 7, 8) + return tuple(range(start, end + 1)) + + try: + return (int(value),) + except ValueError as int_cast_error: + raise ValueError( + f"Invalid order specifier '{value}': should be a single " + "integer 'X'" + ) from int_cast_error + + +def raise_on_duplicates(ordered_pairs: list[tuple[Any, Any]]) -> Optional[Any]: + """Reject duplicate keys.""" + d = {} + for k, v in ordered_pairs: + if k in d: + raise ValueError(f"Duplicate key: {k}") + d[k] = v + return d diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generation_with_numInvalidOK.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generation_with_numInvalidOK.py new file mode 100644 index 000000000..d51bcd4b0 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generation_with_numInvalidOK.py @@ -0,0 +1,1168 @@ +"""Incorporate numInvalidOKs into Test Table + +Classes: + TTGWithEDAPathConfig + TTGwithEDAOrchestrator + MultiOrderOrCommandTTGwithEDA + ResumableTTGwithEDA +""" + +__all__ = [] + +from abc import ABC, abstractmethod +import argparse +from dataclasses import dataclass +import json +from os import walk +from pathlib import Path +import platform +import re +import sys +import time +from typing import Optional + +from edge_deletion_analysis import ( + EdgeDeletionAnalyzer, +) +from planaritytesting_utils import ( + parse_range, + is_path_to_executable, + PLANARITY_ALGORITHM_SPECIFIERS, + EDGE_DELETION_ANALYSIS_SPECIFIERS, + max_num_edges_for_order, + raise_on_duplicates, +) +from test_table_generator import ( + TestTableGenerator, +) + + +@dataclass +class TTGWithEDAPathConfig: + """Dataclass storing attributes pertaining to path config for TTA with EDA + + Args: + planarity_path: Path to planarity executable to use when running + edge-deletion analysis. If none provided, defaults to: + {planarity_project_root}/Release/planarity + graph_dir: Path to parent directory containing subdirectories for + each graph order, the contents of which are .g6 graph files, + one for each edgecount from 0 to (N * (N - 1)) / 2. If none + provided, defaults to: + TestSupport/results/graph_generation_orchestrator/ + planarity_output_dir: Path to parent directory containing + subdirectories for each graph order, the contents of which are + subdirectories for each graph algorithm extension. Each of + these directories contains the results of running planarity + Test All Graphs for the given command on all graphs of a given + order and number of edges. If none provided, defaults to: + TestSupport/results/planarity_testAllGraphs_orchestrator/ + test_table_output_dir: Parent directory under which subdirectory + named after each {order} will be created for output results. If + none provided, defaults to: + TestSupport/tables/ + canonical_files: Whether or not the .g6 files are in canonical form + makeg_files: Whether or not the .g6 files were generated by makeg + + Raises: + ValueError: + - planarity_path doesn't correspond to an executable + - the graph_dir is not a directory + - the planarity_output_dir is not a directory + - the test_table_output_dir is not a directory + """ + + planarity_executable_path: Optional[Path] = None + graph_dir: Optional[Path] = None + planarity_output_dir: Optional[Path] = None + test_table_output_dir: Optional[Path] = None + canonical_files: bool = False + makeg_files: bool = False + + def __post_init__(self): + """Custom post-init logic to normalize and validate path config""" + self.planarity_project_root: Path = ( + Path(sys.argv[0]).resolve().parent.parent.parent + ) + self._normalize_and_validate_planarity_executable_path() + self._normalize_and_validate_graph_dir() + self._normalize_and_validate_planarity_output_dir() + self._normalize_and_validate_test_table_output_dir() + + def _normalize_and_validate_planarity_executable_path(self) -> None: + """Ensure planarity path refers to a valid executable + + Raises: + ValueError: + - planarity_path cannot be resolved + - planarity_path doesn't corespond to an executable + """ + if not self.planarity_executable_path: + extension = ".exe" if platform.system() == "Windows" else "" + self.planarity_executable_path = Path.joinpath( + self.planarity_project_root, "Release", f"planarity{extension}" + ) + try: + self.planarity_executable_path = ( + self.planarity_executable_path.resolve() + ) + except OSError as planarity_path_resolve_error: + raise ValueError( + "Path to planarity executable " + f"'{self.planarity_executable_path}' cannot be resolved." + ) from planarity_path_resolve_error + if not is_path_to_executable(self.planarity_executable_path): + raise ValueError( + "Path for planarity executable " + f"'{self.planarity_executable_path}' does not correspond to " + "an executable." + ) + + def _normalize_and_validate_graph_dir(self) -> None: + """Set default for graph_dir and ensure directory exists + + Raises: + ValueError: if self.graph_dir.is_dir() is false + """ + if not self.graph_dir: + self.graph_dir = Path.joinpath( + self.planarity_project_root, + "TestSupport", + "results", + "graph_generation_orchestrator", + ) + if not self.graph_dir.is_dir(): + raise ValueError( + f"graph_dir = '{self.graph_dir}' does not correspond to a " + "directory." + ) + + def _normalize_and_validate_planarity_output_dir(self) -> None: + """Set default for planarity_output_dir and ensure directory exists + + Raises: + ValueError: if self.planarity_output_dir.is_dir() is false + """ + if not self.planarity_output_dir: + self.planarity_output_dir = Path.joinpath( + self.planarity_project_root, + "TestSupport", + "results", + "planarity_testAllGraphs_orchestrator", + ) + if not self.planarity_output_dir.is_dir(): + raise ValueError( + f"planarity_output_dir = '{self.planarity_output_dir}' does " + "not correspond to a directory." + ) + + def _normalize_and_validate_test_table_output_dir(self) -> None: + """Set default for test_table_output_dir and ensure directory exists + + Raises: + ValueError: if self.test_table_output_dir.is_dir() is false + """ + if not self.test_table_output_dir: + self.test_table_output_dir = Path.joinpath( + self.planarity_project_root, + "TestSupport", + "tables", + ) + if not self.test_table_output_dir.is_dir(): + raise ValueError( + f"test_table_output_dir = '{self.test_table_output_dir}' " + "does not correspond to a directory." + ) + + def _input_dir_contains_expected_files( + self, order: int, input_dir: Path + ) -> bool: + """Determines if files in input_dir are named in the expected scheme + + Args: + order: Files in input_dir are expected to pertain to graphs of the + given order + input_dir: Directory containing files to check + + Returns: + boolean indicating whether (True) or not (False) all files match + the expected naming scheme, and all expected files are present + """ + infile_candidate_prefixes = sorted( + ".".join(infile_candidate.name.split(".")[:2]) + for infile_candidate in input_dir.iterdir() + if (self.canonical_files == ("canonical" in infile_candidate.name)) + if (self.makeg_files == ("makeg" in infile_candidate.name)) + ) + expected_infile_prefixes = sorted( + f"n{order}.m{num_edges}" + for num_edges in range(max_num_edges_for_order(order) + 1) + ) + return infile_candidate_prefixes == expected_infile_prefixes + + def check_graph_dir_contents(self, orders: tuple[int, ...]) -> None: + """Ensure graph_dir contains subdirectories for each order + + graph_dir must be a Path to parent directory containing subdirectories + for each graph order, the contents of which are .g6 graph files, one + for each edgecount from 0 to (N * (N - 1)) / 2 + + Args: + orders: The graph orders for which you wish to perform EDA and + compile tables + + Raises: + ValueError: If the graph_dir + 1. contains files, + 2. contains no subdirectories + 3. is missing a subdirectory for one of the desired orders + Or if the files in the subdirectories are not named in + the expected scheme + + Returns: + Path object graph_dir + """ + assert self.graph_dir is not None + for dirpath, dirnames, filenames in walk(self.graph_dir): + if len(filenames) > 0: + raise ValueError( + f"graph_dir = '{self.graph_dir}' has unexpected " + "structure: should only contain subdirectories, one for " + f"each graph order in {orders}." + ) + if len(dirnames) == 0: + raise ValueError( + f"graph_dir = '{self.graph_dir}' has no subdirectories: " + "expected one subdirectory for each graph order in " + f"{orders}." + ) + for order in orders: + if str(order) not in dirnames: + raise ValueError( + f"graph_dir = '{self.graph_dir}' is missing " + f"subdirectory for graph order N={order}." + ) + graph_dir_for_order = Path.joinpath( + Path(dirpath).resolve(), f"{order}" + ) + if not self._input_dir_contains_expected_files( + order, graph_dir_for_order + ): + raise ValueError( + f"Graph dir for order '{graph_dir_for_order}' doesn't " + "contain the expected .g6 infiles (i.e. one per each " + f"num_edges for the given order {order})." + ) + # Don't recurse into subdirectories + break + + def check_planarity_output_dir_contents( + self, + orders: tuple[int, ...], + generate_tables_for_commands: tuple[str, ...], + ) -> None: + """Check planarity output dir has expected subdirectory structure + + planarity_output_dir should be a Path object corresponding to a parent + directory containing subdirectories for each graph order, the contents + of which are subdirectories for each graph algorithm extension. Each of + these directories contains the results of running planarity Test All + Graphs for the given command on all graphs of a given order and number + of edges + + Args: + orders: The graph orders for which you wish to perform EDA and + compile tables + generate_tables_for_commands: The graph algorithm commands for + which you wish to perform EDA (if applicable) and generate + tables + + Raises: + ValueError: If the planarity_output_dir: + 1. contains files, + 2. contains no subdirectories + 3. is missing a subdirectory for one of the desired orders + If, for each of the subdirectories of planarity_output_dir + containing the results for a specific graph order, the + directory: + 1. contains files, + 2. contains no subdirectories + 3. is missing a subdirectory for one of the graph algoritm + command specifiers + Or if the files in the sub-sub directories are not named in + the expected scheme + """ + assert self.planarity_output_dir is not None + for dirpath, dirnames, filenames in walk(self.planarity_output_dir): + if len(filenames) > 0: + raise ValueError( + f"planarity_output_dir = '{self.planarity_output_dir}' " + "has unexpected structure: should only contain " + "subdirectories, one for each graph order in " + f"{orders}." + ) + if len(dirnames) == 0: + raise ValueError( + f"planarity_output_dir = '{self.planarity_output_dir}' " + "has no subdirectories: expected one subdirectory for " + f"each graph order in {orders}." + ) + for order in orders: + if str(order) not in dirnames: + raise ValueError( + f"planarity_output_dir = '{self.planarity_output_dir}'" + f" is missing subdirectory for graph order N={order}." + ) + for dirname in dirnames: + if int(dirname) not in orders: + continue + planarity_output_dir_for_order = Path.joinpath( + Path(dirpath).resolve(), dirname + ) + for _, subdirs, subdir_filenames in walk( + planarity_output_dir_for_order + ): + if len(subdir_filenames) > 0: + raise ValueError( + "Planarity output subdirectory " + f"'{planarity_output_dir_for_order}' should only " + "contain subdirectories." + ) + if len(dirnames) == 0: + raise ValueError( + "Planarity output subdirectory " + f"'{planarity_output_dir_for_order}' has no " + "subdirectories: expected one subdirectory for " + "each command specifier in " + f"{generate_tables_for_commands}." + ) + for command in generate_tables_for_commands: + if command not in subdirs: + raise ValueError( + "Planarity output subdirectory " + f"'{planarity_output_dir_for_order}' is " + "missing subdirectory for command " + f"'{command}'." + ) + output_dir_for_order_and_command = Path.joinpath( + planarity_output_dir_for_order, f"{command}" + ) + if not self._input_dir_contains_expected_files( + int(dirname), + output_dir_for_order_and_command, + ): + raise ValueError( + f"Planarity output dir for order and command " + f"'{output_dir_for_order_and_command}' " + "doesn't contain the expected planarity " + "output files (i.e. one per each " + f"num_edges for the given order {dirname})." + ) + # Don't recurse into subdirectories + break + # Don't recurse into subdirectories + break + + +class TTGwithEDAOrchestrator: + """Orchestrate test table generation with results of edge-deletion analysis + + Validate command-line parameters and, based on their values, instantiate + MultiOrderTTGwithEDA or ResumableTTGwithEDA. + """ + + def __init__( + self, + path_config: TTGWithEDAPathConfig, + orders: Optional[tuple[int, ...]] = None, + edgecount_limit: Optional[int] = None, + generate_tables_for_commands: Optional[tuple[str, ...]] = None, + ) -> None: + """Initializes TTGwithEDAOrchestrator instance + + Args: + path_config: An instance of TTGWithEDAPathConfig that contains the + validated and normalized Paths for planarity_path, graph_dir, + planarity_output_dir, and test_table_output_dir, as well as the + boolean flags representing whether we are performing the work + on canonical_files and/or makeg_files. + orders: Graph orders for which to perform edge-deletion analysis + for chosen graph algorithm extensions and to tabulate results + of running planarity Test All Graphs + edgecount_limit: When generating table for single graph order and + command specifier, indicates the maximum edgecount for which + you wish to perform EDA; must be less than or equal to + (N * (N - 1)) / 2, and if a resume-file exists, must be + strictly greater than the highest previously processed edge- + count + generate_tables_for_commands: Command specifiers for which you wish + to generate test tables; defaults to tuple returned by + PLANARITY_ALGORITHM_SPECIFIERS() + + Raises: + ValueError: + - the specified graph orders are invalid + - the command specifiers for which you wish to perform EDA (if + applicable) and generate the respective test tables are invalid + - the edgecount limit is invalid, in light of the chosen graph + order(s) and command specifier(s) + - the graph_dir does not have the correct structure + - the planarity_output_dir does not have the expected structure + """ + self.orders = self._validate_orders(orders) + self.generate_tables_for_commands = self._validate_command_specifiers( + generate_tables_for_commands + ) + self.edgecount_limit = self._validate_edgecount_limit(edgecount_limit) + path_config.check_graph_dir_contents(self.orders) + path_config.check_planarity_output_dir_contents( + self.orders, self.generate_tables_for_commands + ) + self.path_config = path_config + self.ttg_with_eda_driver: Optional[TTGwithEDA] = None + + def _validate_orders( + self, orders: Optional[tuple[int, ...]] + ) -> tuple[int, ...]: + """Validating graph orders for which you wish to run EDA and do TTG + + Raises: + ValueError: + - orders tuple contains non-integer values + - the largest graph order specified in a range > 9 + Returns: + tuple containing user-specified graph order(s) + """ + if orders is None or len(orders) == 0: + orders = tuple(range(6, 10)) # orders defaults to (6, 7, 8, 9) + if not all(isinstance(x, int) for x in orders): + raise ValueError("Orders tuple contains non-integer values.") + if len(orders) > 1 and max(orders) > 9: + raise ValueError( + "When specifying a range of orders, the maximum order must " + "not exceed 9, otherwise the system will run out of memory." + ) + return orders + + def _validate_command_specifiers( + self, generate_tables_for_commands: Optional[tuple[str, ...]] + ) -> tuple[str, ...]: + """Validating commands for which you wish to generate test tables. + + Args: + generate_tables_for_commands: tuple of str indicating the graph + algorithm extensions for which you wish to generate tables + + Raises: + ValueError: + - self.orders is None or empty (falsy) + - invalid command specifiers given + - multiple command specifiers are given when graph order > 9 + + Returns: + tuple of valid command specifiers + """ + if self.orders is None or len(self.orders) == 0: + raise ValueError("Invalid self.orders tuple.") + if ( + generate_tables_for_commands is None + or len(generate_tables_for_commands) == 0 + ): + generate_tables_for_commands = PLANARITY_ALGORITHM_SPECIFIERS() + if not all( + (c in PLANARITY_ALGORITHM_SPECIFIERS()) + for c in generate_tables_for_commands + ): + raise ValueError( + "Invalid command specifier(s) given when indicating which " + "algorithms for which you wish to generate test tables." + ) + # NOTE: if (max(self.orders) > 9) holds, self.orders should only + # contain one element, since self._validate_orders() would throw an + # error if the user specified a range of graph orders with the max > 9 + if max(self.orders) > 9 and len(generate_tables_for_commands) > 1: + raise ValueError( + "When the specified graph order is greater than 9, must only " + "choose one algorithm command specifier for which you wish to " + "perform edge-deletion analysis and generate table." + ) + return generate_tables_for_commands + + def _orders_and_commands_are_valid(self) -> bool: + """Ensure orders and generate_tables_for_commands are nonempty""" + if ( + self.orders is None + or len(self.orders) == 0 + or self.generate_tables_for_commands is None + or len(self.generate_tables_for_commands) == 0 + ): + return False + return True + + def _validate_edgecount_limit( + self, edgecount_limit: Optional[int] + ) -> Optional[int]: + """Ensure edgecount limit is valid relative to other options + + Args: + edgecount_limit: max edge-count up to which you wish to perform EDA + + Raises: + ValueError: + - self.orders or self.generate_tables_for_commands are somehow + None or empty tuples (falsy) + - edgecount_limit is given when multiple graph orders and/or + commands are specified + - edgecount_limit exceeds the maximum possible for the given + graph order + + Returns: + int edgecount_limit (for single order and command) or None (if + not applicable, i.e. when running multiple orders/commands) + """ + if not self._orders_and_commands_are_valid(): + raise ValueError( + "Unable to validate edgecount limit: invalid self.orders or " + "self.generate_tables_for_commands tuples." + ) + if len(self.orders) > 1 or len(self.generate_tables_for_commands) > 1: + if edgecount_limit is not None: + raise ValueError( + "Cannot specify an edgecount limit when wanting to " + "perform EDA and generate tables for multiple graph " + "orders and/or graph algorithm commands." + ) + else: + assert self.orders is not None and len(self.orders) == 1 + edgecount_limit = self._normalize_edgecount_limit( + self.orders[0], edgecount_limit + ) + return edgecount_limit + + def _normalize_edgecount_limit( + self, order: int, edgecount_limit: Optional[int] + ) -> int: + """For a given graph order, normalize and validate the edgecount_limit + + Args: + order: The graph order against which you wish to check whether the + edgecount_limit is valid + edgecount_limit: either None (will be set to (N*(N-1))/2) or some + int we wish to verify is within the accepted range + + Raises: + ValueError: + - invalid graph order supplied + - edgecount_limit exceeds (N*(N-1))/2 + + Returns: + Either the original int edgecount_limit if it is within the + accepted range, or (N*(N-1))/2) for the given order + """ + if order < 1: + raise ValueError( + "Unable to normalize edgecount limit given invalid graph " + f"order {order}" + ) + max_edgecount_for_order = max_num_edges_for_order(order) + edgecount_limit = ( + edgecount_limit + if (edgecount_limit is not None and edgecount_limit > -1) + else max_edgecount_for_order + ) + if edgecount_limit > max_edgecount_for_order: + raise ValueError( + f"Given edgecount argument {edgecount_limit} exceeds the " + f"maximum edgecount for order {order}; please " + "choose an edgecount limit less than or equal to " + f"{max_edgecount_for_order}." + ) + return edgecount_limit + + def initialize_driver(self) -> None: + """Initialize correct driver for TTG with EDA based on init parameters""" + if not self._orders_and_commands_are_valid(): + raise ValueError( + "Unable to initialize driver for test table generation with " + "numInvalidOKs due to invalid order or command tuples." + ) + if len(self.orders) > 1 or len(self.generate_tables_for_commands) > 1: + self.ttg_with_eda_driver = MultiOrderOrCommandTTGwithEDA( + path_config=self.path_config, + orders=self.orders, + generate_tables_for_commands=self.generate_tables_for_commands, + ) + elif ( + len(self.orders) == 1 + and len(self.generate_tables_for_commands) == 1 + ): + assert self.edgecount_limit is not None + self.ttg_with_eda_driver = ResumableTTGwithEDA( + path_config=self.path_config, + order=self.orders[0], + generate_tables_for_command=self.generate_tables_for_commands[ + 0 + ], + edgecount_limit=self.edgecount_limit, + ) + + def generate_test_tables_with_invalidOKs(self) -> None: + """Leverage polymorphism to invoke correct behaviour based on params""" + assert self.ttg_with_eda_driver is not None + self.ttg_with_eda_driver.determine_numInvalidOKs() + self.ttg_with_eda_driver.generate_tables_with_numInvalidOKs() + + +class TTGwithEDA(ABC): + """Abstract base class defining common behaviour of TTG with EDA""" + + @abstractmethod + def __init__( + self, + path_config: TTGWithEDAPathConfig, + ) -> None: + self.path_config = path_config + self._numInvalidOKs = {} + + @abstractmethod + def determine_numInvalidOKs(self) -> None: + """Override in subclass to define logic for running EDA""" + + def _determine_numInvalidOKs( + self, order: int, num_edges: int, command: str + ) -> None: + """Set up correct infile name and call EDA + + Args: + order: The graph order for which we wish to perform EDA + num_edges: The number of edges of the graphs in the .g6 file + command: The command specifier for which we wish to perform EDA + """ + canonical_ext = ( + ".canonical" if self.path_config.canonical_files else "" + ) + makeg_ext = ".makeg" if self.path_config.makeg_files else "" + + assert self.path_config.graph_dir is not None + g6_infile_path = Path.joinpath( + self.path_config.graph_dir, + f"{order}", + f"n{order}.m{num_edges}{makeg_ext}{canonical_ext}.g6", + ) + self._call_edge_deletion_analysis(order, command, g6_infile_path) + + def _call_edge_deletion_analysis( + self, order: int, command: str, infile_path: Path + ) -> None: + """For given command, get numInvalidOKs for each order and edge-count + + Args: + order: The order of the graphs in the input file + command: string indicating graph search algorithm command specifier + for which we wish to determine the number of invalid OKs using + the edge-deletion analysis tool. + infile_path: The Path of the .g6 input file on which you wish to + perform the edge-deletion analysis + """ + assert self.path_config.planarity_executable_path is not None + eda = EdgeDeletionAnalyzer( + planarity_path=self.path_config.planarity_executable_path, + infile_path=infile_path, + output_dir=None, + extension_choice=command, + ) + start_proc_time = time.process_time_ns() + start_time = time.perf_counter_ns() + numInvalidOK_for_infile = eda.analyze_graphs() + end_proc_time = time.process_time_ns() + end_time = time.perf_counter_ns() + proc_time_s = (end_proc_time - start_proc_time) / 1e9 + tot_time_s = (end_time - start_time) / 1e9 + + infile_name = infile_path.name + eda.logger.info( + "NUMBER OF INVALID OKs in '%s' is %d; took %fs process time and " + "%fs total time", + infile_name, + numInvalidOK_for_infile, + proc_time_s, + tot_time_s, + ) + + # NOTE: Since the json spec disallows int as keys, one must cast the + # order to a str + order_key = str(order) + if not self._numInvalidOKs.get(order_key): + self._numInvalidOKs[order_key] = {} + if not self._numInvalidOKs[order_key].get(command): + self._numInvalidOKs[order_key][command] = {} + self._numInvalidOKs[order_key][command][infile_name] = { + "numInvalidOK": numInvalidOK_for_infile, + "proc_time_s": proc_time_s, + "tot_time_s": tot_time_s, + } + + @abstractmethod + def generate_tables_with_numInvalidOKs(self) -> None: + """Override in subclass to define logic for generating tables""" + + def _generate_table_with_numInvalidOKs_for_order_and_command( + self, order: int, command: str + ) -> None: + """For an order and command, compile planarity output and EDA results + + Args: + order: The graph order for which you wish to generate test table + with numInvalidOKs + command: The algorithm command specifier for which testing was done + """ + assert self.path_config.planarity_output_dir is not None + input_dir_for_order_and_command = Path.joinpath( + self.path_config.planarity_output_dir, f"{order}", f"{command}" + ) + assert self.path_config.test_table_output_dir is not None + ttg = TestTableGenerator( + test_table_input_dir=input_dir_for_order_and_command, + test_table_output_dir=self.path_config.test_table_output_dir, + canonical_files=self.path_config.canonical_files, + makeg_files=self.path_config.makeg_files, + edge_deletion_analysis_results=self._numInvalidOKs.get( + str(order), {} + ).get(command, {}), + ) + ttg.get_order_and_command_from_input_dir() + ttg.process_files() + ttg.write_table_formatted_data_to_file() + + +class MultiOrderOrCommandTTGwithEDA(TTGwithEDA): + """Handles when user specifies a range of orders and/or commands""" + + def __init__( + self, + path_config: TTGWithEDAPathConfig, + orders: tuple[int, ...], + generate_tables_for_commands: tuple[str, ...], + ) -> None: + super().__init__(path_config) + self.orders = orders + self.generate_tables_for_commands = generate_tables_for_commands + + def determine_numInvalidOKs(self) -> None: + """Run EdgeDeletionAnalyzer on all .g6 input files for the given order""" + perform_eda_for_commands = tuple( + c + for c in EDGE_DELETION_ANALYSIS_SPECIFIERS() + if c in self.generate_tables_for_commands + ) + + for order in self.orders: + for command in perform_eda_for_commands: + for num_edges in range(max_num_edges_for_order(order) + 1): + self._determine_numInvalidOKs(order, num_edges, command) + + def generate_tables_with_numInvalidOKs(self) -> None: + """Incorporate edge-deletion analysis results into Test Tables""" + for order in self.orders: + for command in self.generate_tables_for_commands: + self._generate_table_with_numInvalidOKs_for_order_and_command( + order, command + ) + + +class ResumableTTGwithEDA(TTGwithEDA): + """When a user specifies a single graph order and a single algorithm + command specifier (one of (2, 3, 4) corresponding to K_{2, 3}, K_{3, 3}, or + K_4 search), we are able to leverage a resume file to save state after + processing each edge-count. + """ + + def __init__( + self, + path_config: TTGWithEDAPathConfig, + order: int, + generate_tables_for_command: str, + edgecount_limit: int, + ) -> None: + super().__init__(path_config) + self.order = order + self.command = generate_tables_for_command + self.edgecount_limit = edgecount_limit + ( + self._resume_file_path, + self.highest_processed_edgecount, + self._numInvalidOKs, + ) = self._initialize_numInvalidOKs() + + def _initialize_numInvalidOKs( + self, + ) -> tuple[Path, int, dict[int, dict[str, dict[str, dict]]]]: + """Initialize numInvalidOKs based on parameterization and resume file + + Raises: + TypeError: resume_file_path exists but corresponds to a directory + ValueError: json.load() fails when trying to load the resume file + + Returns: + Path to resume file for order and command, the highest processed + edge-count, and the initialized numInvalidOKs dict. This dict + either is empty if the resume file doesn't exist, or otherwise has + been populated with the validated contents of the resume file. + """ + numInvalidOKs = {} + highest_processed_edgecount = 0 + resume_file_path = self._determine_resume_file_path() + if resume_file_path.exists(): + if resume_file_path.is_dir(): + raise TypeError( + f"Path to resume file for order {self.order} and command " + f"{self.command} corresponds to a directory. Please " + f"delete '{resume_file_path}' and retry." + ) + with open(resume_file_path, "r", encoding="utf-8") as resume_file: + try: + resume_file_contents = json.load( + resume_file, object_pairs_hook=raise_on_duplicates + ) + except json.JSONDecodeError as invalid_resume_file_error: + raise ValueError( + "Unable to load resume file contents from " + f"path '{resume_file_path}'; please delete and " + "retry." + ) from invalid_resume_file_error + highest_processed_edgecount = self._validate_resume_file( + resume_file_contents + ) + numInvalidOKs = resume_file_contents + return resume_file_path, highest_processed_edgecount, numInvalidOKs + + def _determine_resume_file_path(self) -> Path: + """If command line parameters allow, what resume file path to use?""" + resume_file_dir = Path.joinpath( + self.path_config.planarity_project_root, + "TestSupport", + "results", + "test_table_generation_with_numInvalidOK", + ) + Path.mkdir(resume_file_dir, parents=True, exist_ok=True) + + return resume_file_dir / (f"N_{self.order}-C_{self.command}" ".json") + + def _validate_resume_file( + self, resume_file_contents: dict[str, dict[str, dict[str, dict]]] + ) -> int: + """Determine if resume file contents are valid for specified params + + Args: + resume_file_contents: the results of json.load() on the resume file + for the current order and command + + Raises: + ValueError: if the resume file contents don't match: + { + : + { + "": + { + + } + } + } + + Returns: + The highest_processed_edgecount determined when validating the dict + """ + highest_processed_edgecount = 0 + if len(resume_file_contents) > 1: + raise ValueError( + f"Resume file '{self._resume_file_path}' is invalid; " + "contains multiple top-level keys: " + f"'{resume_file_contents.keys()}'. Should only contain one " + "graph order." + ) + if len(resume_file_contents) == 1: + contents_for_order = resume_file_contents.get(str(self.order), {}) + if len(contents_for_order) > 1: + raise ValueError( + f"Resume file '{self._resume_file_path}' is invalid; " + "contains multiple secondary keys: " + f"'{contents_for_order.keys()}'. Should only contain one " + "command specifier." + ) + if len(contents_for_order) == 1: + contents_for_command = contents_for_order.get(self.command, {}) + if contents_for_command: + highest_processed_edgecount = ( + self._validate_numInvalidOK_data(contents_for_command) + ) + return highest_processed_edgecount + + def _validate_numInvalidOK_data( + self, contents_for_order_and_command: dict[str, dict] + ) -> int: + """Check data for each previously processed edge-count in resume file + + Args: + contents_for_order_and_command: dict; maps infile name to EDA data + + Raises: + ValueError: If the contents for the given order and command don't + match: + { + "": + { + "numInvalidOK": numInvalidOK, + "proc_time_s": proc_time_s, + "tot_time_s": tot_time_s + }, + "": + { + "numInvalidOK": numInvalidOK, + "proc_time_s": proc_time_s, + "tot_time_s": tot_time_s + }, + ... + "": + { + "numInvalidOK": numInvalidOK, + "proc_time_s": proc_time_s, + "tot_time_s": tot_time_s + } + } + + Returns: + The highest_processed_edgecount determined when validating the dict + """ + highest_processed_edgecount = 0 + processed_edgecounts = [] + for infile_name in contents_for_order_and_command: + match = re.match( + r"n(?P\d+)\.m(?P\d+)", + infile_name, + ) + if not match: + raise ValueError( + "Invalid infile name in resume file: " f"'{infile_name}'." + ) + order_from_infile = match.group("order") + if not order_from_infile: + raise ValueError( + "Invalid infile name in resume file: " + f"'{infile_name}' is missing graph order." + ) + try: + order_from_infile = int(order_from_infile) + except TypeError as order_from_infile_conversion_error: + raise ValueError( + "Unable to convert order extracted from " + "infile to int; received " + f"'{order_from_infile}'" + ) from order_from_infile_conversion_error + if order_from_infile != self.order: + raise ValueError( + "Invalid infile name in resume file: " + f"'{infile_name}' corresponds to the wrong " + "graph order." + ) + edgecount_from_infile = match.group("edgecount") + if not edgecount_from_infile: + raise ValueError( + "Invalid infile name in resume file: " + f"'{infile_name}' is missing edgecount." + ) + try: + edgecount_from_infile = int(edgecount_from_infile) + except TypeError as edgecount_from_infile_conversion_error: + raise ValueError( + "Unable to convert edgecount extracted from " + "infile to int; received " + f"'{edgecount_from_infile}'" + ) from edgecount_from_infile_conversion_error + if ( + edgecount_from_infile < 0 + or edgecount_from_infile > self.edgecount_limit + ): + raise ValueError( + "Invalid infile name in resume file: " + f"'{infile_name}' contains edgecount " + f"{edgecount_from_infile} which is outside of range " + f"[0, {self.edgecount_limit}]; either delete resume file " + "or adjust edge-count limit." + ) + highest_processed_edgecount = max( + highest_processed_edgecount, edgecount_from_infile + ) + processed_edgecounts.append(edgecount_from_infile) + processed_edgecounts.sort() + for x, y in zip( + processed_edgecounts, + range(0, max(processed_edgecounts) + 1), + ): + if x != y: + raise ValueError( + "Invalid resume file contents: missing data " + f"for edgecount {y}; delete the resume file " + "and retry." + ) + if highest_processed_edgecount == self.edgecount_limit: + response = input( + "Highest processed edge-count is equal to specified edge-" + "count limit; do you wish to proceed (yY)?\n" + "\t(If not, press any other key to abort, then delete the " + "existing resume file)\n" + ) + if response.lower() != "y": + sys.exit(0) + return highest_processed_edgecount + + def determine_numInvalidOKs(self) -> None: + """Define logic for running EDA while interacting with resume file""" + starting_edgecount = ( + self.highest_processed_edgecount + 1 + if self.highest_processed_edgecount > 0 + else 0 + ) + for num_edges in range(starting_edgecount, self.edgecount_limit + 1): + self._determine_numInvalidOKs(self.order, num_edges, self.command) + assert self._resume_file_path is not None + Path.mkdir( + self._resume_file_path.parent, exist_ok=True, parents=True + ) + with open( + self._resume_file_path, "w", encoding="utf-8" + ) as resume_file: + json.dump(self._numInvalidOKs, resume_file, indent=4) + + def generate_tables_with_numInvalidOKs(self) -> None: + """Define logic for generating table for single order and command""" + self._generate_table_with_numInvalidOKs_for_order_and_command( + order=self.order, command=self.command + ) + Path.unlink(self._resume_file_path, missing_ok=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="Test Table Generation including numInvalidOKs\n\n" + "For each specified graph order:\n" + "1. For each edgecount from 0 to (N * (N - 1))/2, runs the " + "EdgeDeletionAnalyzer for K_{2, 3} search, K_{3, 3} search, and K_4 " + "search to determine the numInvalidOKs for those graph algorithm " + "extensions.\n" + "2. For each of the supported graph algorithms, Tabulates results " + "from running planarity's Test All Graphs functionality, " + "incorporating the numInvalidOKs if applicable.\n", + ) + parser.add_argument( + "-p", + "--planaritypath", + type=Path, + required=False, + help="Path to planarity executable. Defaults to:\n" + "\t{planarity_project_root}/Release/planarity", + metavar="PATH_TO_PLANARITY_EXECUTABLE", + ) + parser.add_argument( + "-n", + "--orders", + type=parse_range, + required=False, + help="Order(s) of graphs for which you wish to generate Test Tables; " + f"for commands {EDGE_DELETION_ANALYSIS_SPECIFIERS()}, will include " + "numInvalidOKs derived by edge-deletion analysis.", + metavar="X[,Y]", + ) + parser.add_argument( + "-e", + "--edgecountlimit", + type=int, + required=False, + help="If only a single graph order and graph search algorithm are " + "specified, perform EDA for edge-counts up to and including this " + "value.", + metavar="M", + ) + parser.add_argument( + "-c", + "--gentablecommands", + nargs="+", + default=[], + required=False, + help="List of algorithm command specifiers for which you wish to " + "generate test tables. Defaults to " + f"{PLANARITY_ALGORITHM_SPECIFIERS()}", + ) + parser.add_argument( + "-g", + "--graphdir", + type=Path, + required=False, + help="Path to parent directory containing subdirectories for each " + "graph order, the contents of which are assumed to be .g6 files (one " + "for each number of edges from 0 to (N * (N - 1))/2). Defaults to:\n" + "\tTestSupport/results/graph_generation_orchestrator/", + metavar="PATH_TO_GRAPH_PARENT_DIR", + ) + parser.add_argument( + "-t", + "--planarityoutputdir", + type=Path, + required=False, + help="Path to parent directory containing subdirectories for each " + "graph order, the contents of which are subdirectories for each graph " + f"algorithm extension (i.e. {PLANARITY_ALGORITHM_SPECIFIERS()}). Each " + "of these directories contains the results of running planarity Test " + "All Graphs for the given command on all graphs of a given order and " + "number of edges. Defaults to:\n" + "\tTestSupport/results/planarity_testAllGraphs_orchestrator/\n" + "The directories will have the form:\n" + "\t{planarity_output_dir}/{order}/{command}/\n" + "And will contain files with names of the form:\n" + "\tn{order}.m{num_edges}(.makeg)?(.canonical)?.{command}.out.txt", + metavar="PATH_TO_PLANARITY_OUTPUT", + ) + parser.add_argument( + "-o", + "--outputdir", + type=Path, + default=None, + metavar="OUTPUT_DIR_PATH", + help="Parent directory under which subdirectory named after each" + "{order} will be created for output results. If none provided, " + "defaults to:\n" + "\tTestSupport/tables/\n" + "For each chosen order, output file paths have the form:\n" + "\t{output_dir}/{order}/n{order}.mALL.{command}.out.txt", + ) + parser.add_argument( + "-l", + "--canonicalfiles", + action="store_true", + help="Indicates .g6 input files are in canonical form", + ) + parser.add_argument( + "-m", + "--makegfiles", + action="store_true", + help="Indicates .g6 input files were generated by makeg", + ) + args = parser.parse_args() + path_config_from_args = TTGWithEDAPathConfig( + planarity_executable_path=args.planaritypath, + graph_dir=args.graphdir, + planarity_output_dir=args.planarityoutputdir, + test_table_output_dir=args.outputdir, + canonical_files=args.canonicalfiles, + makeg_files=args.makegfiles, + ) + eda_ttg_orchestrator = TTGwithEDAOrchestrator( + path_config=path_config_from_args, + orders=args.orders, + edgecount_limit=args.edgecountlimit, + generate_tables_for_commands=args.gentablecommands, + ) + eda_ttg_orchestrator.initialize_driver() + eda_ttg_orchestrator.generate_test_tables_with_invalidOKs() diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generator.py b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generator.py new file mode 100644 index 000000000..8ecf72331 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/TestSupport/planaritytesting/test_table_generator.py @@ -0,0 +1,610 @@ +"""Compile planarity testAllGraphs() output into table + +Classes: + TestTableGeneratorPathError + TestTableGeneratorFileProcessingError + TestTableGenerator +""" + +#!/usr/bin/env python + +__all__ = [ + "TestTableGeneratorPathError", + "TestTableGeneratorFileProcessingError", + "TestTableGenerator", +] + +import sys +import argparse +from pathlib import Path +from typing import Optional + +from planaritytesting_utils import PLANARITY_ALGORITHM_SPECIFIERS +from planarity_testAllGraphs_output_parsing import ( + TestAllGraphsPathError, + TestAllGraphsOutputFileContentsError, + validate_infile_name, + process_file_contents, +) + + +class TestTableGeneratorPathError(BaseException): + """ + Custom exception signalling issues with the paths of input for the Test + Table Generator. + + For example: + - input_dir or output_dir don't correspond to directories that exist + - input filename contains an order or command that doesn't align with the + order and command extracted from the parts of the parent directory + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class TestTableGeneratorFileProcessingError(BaseException): + """ + Custom exception signalling issues with the file contents of input for the + Test Table Generator. + """ + + def __init__(self, message): + super().__init__(message) + self.message = message + + +class TestTableGenerator: # pylint: disable=too-many-instance-attributes + """Process output from running planarity testAllGraphs for a single command""" + + __COL_WIDTH_TOLERANCE = 2 + + def __init__( + self, + test_table_input_dir: Path, + test_table_output_dir: Path, + canonical_files: bool = False, + makeg_files: bool = False, + edge_deletion_analysis_results: Optional[dict[str, dict]] = None, + ): + """Initializes TestTableGenerator instance. + + Checks that input_dir exists and is a directory, and that it contains + input files. Also checks that the output_dir exists and is a directory. + + Args: + input_dir: Directory containing the files output by running the + planarity Test All Graphs functionality for a given algorithm + command on .g6 input files containing all graphs of a given + order and edge count. + output_dir: Directory to which you wish to write the output file + containing the tabulated results compiled from the input files + in the input_dir. + canonical_files: Optional bool to indicate planarity output + corresponds to canonical format + makeg_files: Optional bool to indicate planarity output corresponds + to makeg .g6 + edge_deletion_analysis_results: (Optional) When generating the test + table for K_{2, 3} search, K_{3, 3} search, or K_4 search, one + may optionally pass in a dictionary mapping input filenames to + a sub-dict containing the numInvalidOK count derived by running + the EdgeDeletionAnalyzer for this graph algorithm extension, + the sum of the system and user CPU time of the current process + (proc_time_s), and the total time elapsed during measurement + (tot_time_s). + + Raises: + TestTableGeneratorPathError: If the input_dir doesn't correspond to + a directory, or if it is empty; or if the output_dir doesn't + correspond to a directory. + """ + self.order = None + self.command = None + + # According to PEP-8, one must use one leading underscore only for + # non-public methods and instance variables. + self._processed_data = {} + self._totals = {} + + if not Path.is_dir(test_table_input_dir): + raise TestTableGeneratorPathError( + f"'{test_table_input_dir}' is not a valid directory." + ) + + if not any(test_table_input_dir.iterdir()): + raise TestTableGeneratorPathError( + f"'{test_table_input_dir}' contains no files." + ) + + self.input_dir = test_table_input_dir + + if Path.is_file(test_table_output_dir): + raise TestTableGeneratorPathError( + f"'{test_table_output_dir}' is not a valid directory." + ) + + self.output_dir = test_table_output_dir + + self.canonical_files = canonical_files + self.makeg_files = makeg_files + self._edge_deletion_analysis_results = ( + edge_deletion_analysis_results + if edge_deletion_analysis_results + else {} + ) + + def get_order_and_command_from_input_dir(self): + """Extract order and command from input_dir if possible + + Takes the self.input_dir pathlib.Path object's parts and attempts to + extract the order and command from the directory structure. If the + input directory is such that the path is of the form + {parent_dir}/{order}/{command}/ + Then we can extract these values early, allowing us to validate + individual input files later in the process. Otherwise, sets + self.order and self.command to None. + """ + parts = self.input_dir.parts + try: + order_from_input_dir = int(parts[-2]) + except ValueError: + order_from_input_dir = None + + command_from_input_dir = parts[-1] + # You may reference class attributes either by the name of the class, + # seen here, or by using "self" + if command_from_input_dir not in PLANARITY_ALGORITHM_SPECIFIERS(): + command_from_input_dir = None + + self.order = order_from_input_dir + self.command = command_from_input_dir + + def process_files(self): + """Process input files to populate _processed_data and _totals dicts + + Iterates over the filenames within the input_dir and constructs the + full infile_path, then calls self._process_file() on this infile_path. + + After all files have been processed, constructs the self._totals dict + containing the compiled results for the numGraphs, numOK, + numNONEMBEDDABLE, numErrors, and duration. Their values are generated + by summing the results of a list-comprehension, which extracts the + values of the same key name in the list of dicts within + self._processed_data.values(). + + If edge_deletion_analysis_results is nonempty, then the values in the + self._processed_data dict for the numInvalidOK count derived by running + the EdgeDeletionAnalyzer for this graph algorithm extension, the sum of + the system and user CPU time of the current process (proc_time_s), and + the total time elapsed during measurement (tot_time_s) will be summed + and added to the self._totals dictionary. + + Raises: + TestTableGeneratorFileProcessingError: If an error occurred + processing the input file corresponding to path infile_path + """ + for dirpath, _, filenames in Path.walk(self.input_dir): + for filename in filenames: + if ( + (self.canonical_files and "canonical" not in filename) + or (not self.canonical_files and "canonical" in filename) + or (self.makeg_files and "makeg" not in filename) + or (not self.makeg_files and "makeg" in filename) + ): + continue + infile_path = Path.joinpath(dirpath, filename) + try: + self._process_file(infile_path) + except ( + TestAllGraphsPathError, + TestAllGraphsOutputFileContentsError, + ) as e: + raise TestTableGeneratorFileProcessingError( + f"Error processing '{infile_path}'." + ) from e + + if not self._processed_data: + raise TestTableGeneratorFileProcessingError( + f"No files in input directory '{self.input_dir}' matched the " + "patterns indicated by the input flags:" + f"\n\tcanonical_files={self.canonical_files}" + f"\n\tmakeg_files={self.makeg_files}" + ) + + self._totals = { + "numGraphs": sum( + int(x.get("numGraphs")) for x in self._processed_data.values() + ), + "numOK": sum( + int(x.get("numOK")) for x in self._processed_data.values() + ), + "numNONEMBEDDABLE": sum( + int(x.get("numNONEMBEDDABLE")) + for x in self._processed_data.values() + ), + "numErrors": sum( + 1 + for x in self._processed_data.values() + if x.get("errorFlag") == "SUCCESS" + ), + "duration": sum( + float(x.get("duration")) for x in self._processed_data.values() + ), + "numInvalidOK": sum( + int(x.get("numInvalidOK")) + for x in self._processed_data.values() + ), + "proc_time_s": sum( + float(x.get("proc_time_s")) + for x in self._processed_data.values() + ), + "tot_time_s": sum( + float(x.get("tot_time_s")) + for x in self._processed_data.values() + ), + } + + def _process_file(self, infile_path: Path): + """Process infile and integrate into self._processed_data dict + + Validates the infile name, then processes file contents and adds them + to the self._processed_data dict by mapping the num_edges to a sub-dict + containing fields for the infilename, numGraphs, numOK, + numNONEMBEDDABLE, errorFlag, and duration. + + If edge_deletion_analysis_results is nonempty, those results (i.e. the + numInvalidOK count, the proc_time_s, and the "wall time" tot_time_s) + are integrated into the self._processed_data dict. + + Args: + infile_path: Corresponds to a file within the input_dir + processed_data: Dict to which we wish to add information parsed + from the infile + + Raises: + TestAllGraphsPathError: If invalid infile_path + TestAllGraphsOutputFileContentsError: If input file corresponds to + results that have already been processed, or re-raises + exception thrown by process_file_contents() + """ + try: + self.order, num_edges, self.command = validate_infile_name( + infile_path, self.order, self.command + ) + except TestAllGraphsPathError as e: + raise TestAllGraphsPathError( + "Unable to process file when given invalid infile name." + ) from e + + if num_edges in self._processed_data: + raise TestAllGraphsOutputFileContentsError( + "Already processed a file corresponding to " + f"{num_edges} edges." + ) + + try: + ( + planarity_infile_name, + duration, + numGraphs, + numOK, + numNONEMBEDDABLE, + errorFlag, + ) = process_file_contents(infile_path, self.command) + except TestAllGraphsOutputFileContentsError as e: + raise TestAllGraphsOutputFileContentsError( + f"Unable to process contents of '{infile_path}'." + ) from e + + self._processed_data[num_edges] = { + "infilename": planarity_infile_name, + "numGraphs": numGraphs, + "numOK": numOK, + "numNONEMBEDDABLE": numNONEMBEDDABLE, + "errorFlag": errorFlag, + "duration": duration, + } + + self._processed_data[num_edges].update( + { + "numInvalidOK": self._edge_deletion_analysis_results.get( + planarity_infile_name, {} + ).get("numInvalidOK", 0), + "proc_time_s": self._edge_deletion_analysis_results.get( + planarity_infile_name, {} + ).get("proc_time_s", 0.0), + "tot_time_s": self._edge_deletion_analysis_results.get( + planarity_infile_name, {} + ).get("tot_time_s", 0.0), + } + ) + + def write_table_formatted_data_to_file( + self, + ): # pylint: disable=too-many-locals disable=too-many-statements + """Writes the data extracted from the input files and totals to table""" + self.output_dir = Path.joinpath(self.output_dir, f"{self.order}") + Path.mkdir(self.output_dir, parents=True, exist_ok=True) + + makeg_ext = ".makeg" if self.makeg_files else "" + canonical_ext = ".canonical" if self.canonical_files else "" + output_path = Path.joinpath( + self.output_dir, + f"n{self.order}.mALL{makeg_ext}{canonical_ext}.{self.command}" + + ".out.txt", + ) + + infilename_heading = "Input filename" + num_edges_heading = "# Edges" + num_graphs_heading = "# Graphs" + numOK_heading = "# OK" + num_NONEMBEDDABLE_heading = "# NONEMBEDDABLE" + errorFlag_heading = "Error flag" + duration_heading = "Duration" + numInvalidOK_heading = "# Invalid OK" + proc_time_s_heading = "EDA Proc Time" + tot_time_s_heading = "EDA Total Time" + + infilename_len = ( + max( + len(infilename_heading), + *[ + len(x.get("infilename")) + for x in self._processed_data.values() + ], + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + num_edges_len = ( + max( + len(num_edges_heading), + *[len(str(num_edges)) for num_edges in self._processed_data], + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + numGraphs_len = ( + max( + len(str(self._totals.get("numGraphs"))), + len(num_graphs_heading), + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + numOK_len = ( + max(len(str(self._totals.get("numOK"))), len(numOK_heading)) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + numNONEMBEDDABLE_len = ( + max( + len(str(self._totals.get("numNONEMBEDDABLE"))), + len(num_NONEMBEDDABLE_heading), + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + errorFlag_len = len(errorFlag_heading) + duration_len = ( + max(len(str(self._totals.get("duration"))), len(duration_heading)) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + + nonempty_edge_deletion_analysis_results = bool( + self._edge_deletion_analysis_results + ) + numInvalidOK_len = 0 + proc_time_s_len = 0 + tot_time_s_len = 0 + if nonempty_edge_deletion_analysis_results: + numInvalidOK_len = ( + max( + len(str(self._totals.get("numInvalidOK"))), + len(numInvalidOK_heading), + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + proc_time_s_len = ( + max( + len(str(self._totals.get("proc_time_s"))), + len(proc_time_s_heading), + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + tot_time_s_len = ( + max( + len(str(self._totals.get("tot_time_s"))), + len(tot_time_s_heading), + ) + + TestTableGenerator.__COL_WIDTH_TOLERANCE + ) + + with open(output_path, "w", encoding="utf-8") as outfile: + # Print the table header + outfile.write( + f"|{infilename_heading:^{infilename_len}}" + f"|{num_edges_heading:^{num_edges_len}}" + f"|{num_graphs_heading:^{numGraphs_len}}" + f"|{numOK_heading:^{numOK_len}}" + f"|{num_NONEMBEDDABLE_heading:^{numNONEMBEDDABLE_len}}" + f"|{errorFlag_heading:^{errorFlag_len}}" + f"|{duration_heading:^{duration_len}}" + ) + if self._edge_deletion_analysis_results: + outfile.write(f"|{numInvalidOK_heading:^{numInvalidOK_len}}") + outfile.write(f"|{proc_time_s_heading:^{proc_time_s_len}}") + outfile.write(f"|{tot_time_s_heading:^{tot_time_s_len}}") + outfile.write("|\n") + + # Print the table header separator + outfile.write( + f"|{'='*infilename_len}" + f"|{'='*num_edges_len}" + f"|{'='*numGraphs_len}" + f"|{'='*numOK_len}" + f"|{'='*numNONEMBEDDABLE_len}" + f"|{'='*len('Error flag')}" + f"|{'='*duration_len}" + ) + if nonempty_edge_deletion_analysis_results: + outfile.write(f"|{'='*numInvalidOK_len}") + outfile.write(f"|{'='*proc_time_s_len}") + outfile.write(f"|{'='*tot_time_s_len}") + outfile.write("|\n") + + # Print the table rows + for num_edges in sorted(self._processed_data.keys()): + data = self._processed_data.get(num_edges) + if data is None: + raise TestTableGeneratorFileProcessingError( + f"Data for M = {num_edges} is missing from processed " + "data dict." + ) + + outfile.write( + f"|{data.get('infilename'):<{infilename_len}}" + f"|{num_edges:<{num_edges_len}}" + f"|{data.get('numGraphs'):<{numGraphs_len}}" + f"|{data.get('numOK'):<{numOK_len}}" + f"|{data.get('numNONEMBEDDABLE'):<{numNONEMBEDDABLE_len}}" + f"|{data.get('errorFlag'):<{errorFlag_len}}" + f"|{data.get('duration'):<{duration_len}}" + ) + if nonempty_edge_deletion_analysis_results: + outfile.write( + f"|{data.get('numInvalidOK'):<{numInvalidOK_len}}" + ) + outfile.write( + f"|{data.get('proc_time_s'):<{proc_time_s_len}.{3}f}" + ) + outfile.write( + f"|{data.get('tot_time_s'):<{tot_time_s_len}.{3}f}" + ) + outfile.write("|\n") + + # Print the table footer separator + outfile.write( + f"|{'='*infilename_len}" + f"|{'='*num_edges_len}" + f"|{'='*numGraphs_len}" + f"|{'='*numOK_len}" + f"|{'='*numNONEMBEDDABLE_len}" + f"|{'='*len('Error flag')}" + f"|{'='*duration_len}" + ) + if nonempty_edge_deletion_analysis_results: + outfile.write(f"|{'='*numInvalidOK_len}") + outfile.write(f"|{'='*proc_time_s_len}") + outfile.write(f"|{'='*tot_time_s_len}") + outfile.write("|\n") + + # Print the totals footer + data = self._totals + outfile.write( + f"|{'TOTALS':<{infilename_len + num_edges_len + 1}}" + f"|{data.get('numGraphs'):<{numGraphs_len}}" + f"|{data.get('numOK'):<{numOK_len}}" + f"|{data.get('numNONEMBEDDABLE'):<{numNONEMBEDDABLE_len}}" + f"|{data.get('numErrors'):<{errorFlag_len}}" + f"|{data.get('duration'):<{duration_len}}" + ) + if nonempty_edge_deletion_analysis_results: + outfile.write( + f"|{data.get('numInvalidOK'):<{numInvalidOK_len}}" + ) + outfile.write( + f"|{data.get('proc_time_s'):<{proc_time_s_len}.{3}f}" + ) + outfile.write( + f"|{data.get('tot_time_s'):<{tot_time_s_len}.{3}f}" + ) + outfile.write("|\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + usage="python %(prog)s [options]", + description="Test Table Generator\n" + "Tabulates results from output files produced by planarity's " + "Test All Graphs functionality.\n\n" + "Expects to be given an input directory containing only the " + "output files produced by running planarity's Test All Graphs " + "for a specific algorithm.\n\n" + "Preferably, this will be the output from having run the " + "Planarity Orchestrator, and will have a path of the form:\n" + "\t{parent_dir}/{order}/{command}/\n" + "And will contain files with the full path:\n" + "\t{parent_dir}/{order}/{command}/n{order}.m{numEdges}(.makeg)?" + "(.canonical)?.{command}.out.txt\n\n" + "Will output one file per graph algorithm containing the " + "tabulated data compiled from the planarity Test All Graphs " + "output files:\n" + "\t{output_dir}/n{order}.mALL(.makeg)?(.canonical)?.{command}.out" + ".txt", + ) + parser.add_argument( + "-i", "--inputdir", type=Path, default=None, metavar="INPUT_DIR" + ) + parser.add_argument( + "-o", "--outputdir", type=Path, default=None, metavar="OUTPUT_DIR" + ) + parser.add_argument( + "-c", + "--command", + type=str, + default="3", + metavar="ALGORITHM_COMMAND", + help="One of (pdo234); defaults to '%(default)s'", + ) + parser.add_argument( + "-n", + "--order", + type=int, + default=11, + metavar="ORDER", + help="Order of graphs for which you wish to compile results of " + "having run planarity with given command specifier. Defaults to " + "N = %(default)s", + ) + parser.add_argument( + "-l", + "--canonicalfiles", + action="store_true", + help="Planarity output files must contain 'canonical' in name", + ) + parser.add_argument( + "-m", + "--makegfiles", + action="store_true", + help="Planarity output files must contain 'makeg' in name", + ) + + args = parser.parse_args() + + command = args.command + order = args.order + + test_support_dir = Path(sys.argv[0]).resolve().parent.parent + if not args.inputdir: + input_dir = Path.joinpath( + test_support_dir, + "results", + "planarity_testAllGraphs_orchestrator", + f"{order}", + f"{command}", + ) + else: + input_dir = Path(args.inputdir).resolve() + + if not args.outputdir: + output_dir = Path.joinpath(test_support_dir, "tables") + else: + output_dir = Path(args.outputdir).resolve() + + ttg = TestTableGenerator( + test_table_input_dir=input_dir, + test_table_output_dir=output_dir, + canonical_files=args.canonicalfiles, + makeg_files=args.makegfiles, + ) + ttg.get_order_and_command_from_input_dir() + ttg.process_files() + ttg.write_table_formatted_data_to_file() diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/.gdbinit b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/.gdbinit new file mode 100644 index 000000000..bc4225620 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/.gdbinit @@ -0,0 +1 @@ +set new-console on diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graph.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graph.h new file mode 100644 index 000000000..3b9a9dd63 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graph.h @@ -0,0 +1,28 @@ +#ifndef HELPERSTUB_GRAPH_H +#define HELPERSTUB_GRAPH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +// NOTE: This helper stub has been added for backwards compatibility to +// support downstream consumers who expect graph.h to be at the +// root of the installed planarity headers directory. Future downstream +// consumers are advised to include the graphLib.h helper stub instead +// because it offers access to all features in the graph library of +// the planarity project. + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "c/graphLib/graph.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib.h new file mode 100644 index 000000000..ccd6f4509 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib.h @@ -0,0 +1,25 @@ +#ifndef HELPERSTUB_GRAPHLIB_H +#define HELPERSTUB_GRAPHLIB_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +// NOTE: This helper stub has been added to make it easier for downstream +// consumers to obtain access to all features in the graph library of +// the planarity project. + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "c/graphLib/graphLib.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c new file mode 100644 index 000000000..883cb7898 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c @@ -0,0 +1,545 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include + +#include "../lowLevelUtils/appconst.h" + +#include "graphExtensions.private.h" +#include "graphExtensions.h" +#include "graphFunctionTable.h" + +/* Imported functions */ + +extern void _InitFunctionTable(graphP theGraph); + +/* Private function */ + +void _FreeExtension(graphExtensionP extension); +void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions); +void _FixupFunctionTables(graphP theGraph, graphExtensionP curr); +graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex); + +/******************************************************************** + * The moduleIDGenerator is used to help ensure that all extensions + * added during a run-time have a different integer identifier. + * An ID identifies an extension, which may be added to multiple + * graphs. It is used in lieu of identifying extensions by a string + * name, which is noticeably expensive when a frequently called + * overload function seeks the extension context for a graph. + ********************************************************************/ + +static int moduleIDGenerator = 0; + +/******************************************************************** + The extension mechanism allows new modules to equip a graph with the + data structures and functions needed to implement new algorithms + without impeding the performance of the core graph planar embedding + algorithms on graphs that have not been so equipped. + + The following steps must be used to create a graph extension: + + 1) Create a moduleID variable initialized to zero that will be + assigned a positive integer the first time the extension is + added to a graph by gp_AddExtension() + + 2) Define an extension context structure to contain all of the data + and function pointers that extend the graph. The context must + include a graphFunctionTable to allow overloading of functions. + An instance of this context structure is passed to the "context" + parameter of gp_AddExtension(). + + 3) Define a function capable of duplicating your context data + structure. It receives a void pointer indicating the context + to duplicate and a void pointer that can be cast to a graph + pointer indicating the graph for which the context is being + duplicated. The void pointer returned indicates the newly + allocated context structure. The pointer to this function is + passed to the "dupContext" parameter of gp_AddExtension() + + Note: It is useful to store in your context structure a pointer + to the graph that the context is extending. There are certain + function overloads you will perform that will only receive + the context, and you may need to know things about the graph, + such as the number of vertices or edges. + + 4) Define a function that can free the memory used by your context + data structure. It will receive a void pointer indicating the + instance of your context data structure that you passed as the + "context" parameter to gp_AddExtension(). + The free function pointer should be passed as the "freeContext" + parameter to gp_AddExtension() + + 5) The expected method of attaching your feature to a graph is to + create a function called gp_AttachFeature(), where 'Feature' is + the name of your module. The attach function allocates your context + data structure, initializes the extension data, assigns overload + function pointers, and invokes gp_AddExtension(). + + NOTE: It is advisable to use memset on the context function table + before assigning any function overloads because any function not + being overloaded must have a NULL pointer. + + NOTE: The gp_AddExtension() method puts the overload function + pointers into the graph's function table, and the base function + pointers that were overloaded are placed in the context function + table. This allows the extension's functions to have access to + base function behaviors, since many extension functions will + extend rather than replace the base behavior. + + 6) There are a few functions that you must overload in order to + successfully manage data structures that are parallel to the + main graph data structures. + + The core graph data structure has function pointers to functions + that can be overloaded. In addition to invoking gp_AddExtension(), + you need to set pointers to your own versions of the functions + you are overloading. You will also need to store a copy of the + prior pointer in your feature's context data structure so that you + can invoke the "base" behavior from your function overload, e.g. + if your feature is attached but not active or if your feature + augments the base behavior rather than replacing it. + + a) If any kind of data structures needs to be maintained at + the graph, vertex or edge levels, then an overload of + fpInitGraph() will be needed. + + b) If any data must be associated with primary and virtual vertices, + then an overload of fpInitVertexRec() is needed. If data must be + associated only with primary vertices (0 to N-1), then one can + overload fpInitVertexInfo() instead. + The overload function should be named _Feature_InitVertexRec() + or _Feature_InitVertexInfo(). + It will invoke the base fpInitVertexRec() or fpInitVertexInfo() + but then also invoke a second function named _InitFeatureVertexRec() + or _InitFeatureVertexInfo() thatinitializes the custom VertexRec + or VertexInfo data members. + + c) If any data must be associated with the edges, then an overload + of fpInitEdgeRec() is needed. + This overload function should be named _Feature_InitEdgeRec(). + It will invoke the base fpInitEdgeRec() and also invoke + a second function named_InitFeatureEdgeRec() that + initializes the custom EdgeRec data members + + d) If any graph-level data structures are needed, then an + overload of fpReinitializeGraph() will also be needed, not just the + overload of fpInitGraph(). However, if only vertex-level and/or + edge level data members are needed, then the overloads of + fpInitVertexRec(), fpInitVertexInfo() and/or fpInitEdgeRec() are + invoked by the basic fpReinitializeGraph without needing to overload + it as well. + + e) If any data must be persisted in the file format, then overloads + of fpReadPostprocess() and fpWritePostprocess() are needed. + + 7) Define internal functions for _Feature_ClearStructures(), + _Feature_CreateStructures() and _Feature_InitStructures(); + + a) The _Feature_ClearStructures() should simply null out pointers + to extra structures on its first invocation, but thereafter it + should free them and then null them. Since the null-only step + is done only once in gp_AttachFeature(), it seems reasonable to + not bother with a more complicated _Feature_ClearStructures(). + But, as an extension is developed, the data structures change, + so it is best to keep all this logic in one place. + + b) The _Feature_CreateStructures() should just allocate memory for + but not initialize any vertex level and edge level data structures. + Data structures maintained at the graph level, such as a stack or a + list collection, should be created _and_ initialized. + + c) The _Feature_InitStructures() should invoke just the functions + needed to initialize the custom VertexRec, VertexInfo and EdgeRec + data members, if any. + + 8) Define a function gp_DetachFeature() that invokes gp_RemoveExtension() + This should be done for consistency, so that users of a feature + do not attach it with gp_AttachFeature() and remove it with + gp_RemoveExtension(). However, it may sometimes be necessary to + run more code than just gp_RemoveExtension() when detaching a feature, + e.g. some final result values of a feature may be saved to data + available in the core graph or in other features. + ********************************************************************/ + +/******************************************************************** + gp_AddExtension() + + @param theGraph - pointer to the graph to which the extension is being added + @param pModuleID - address of the variable that contains the feature's + extension identifier. If the variable is equal to zero, + it is assigned a positive number. Thereafter, the variable + value can be used to find and remove the extension from any graph + @param context - the data storage for the extension being added + The context is owned by the extension and freed with freeContext() + @param dupContext - a function capable of duplicating the context data + @param freeContext - a function capable of freeing the context data + @param functions - pointer to a table of functions stored in the data context. + The table of functions is an input and output parameter. + On input, the table consists of new function pointers + for functions being overloaded. + Any function not being overloaded must be NULL. + The non-NULL function pointers are used to overload + the functions in the graph, and the prior pointer values + in the graph are stored in the function table as output. + The context data therefore has the pointer to the base + function corresponding to any function its extension + module overloaded. + + The new extension is created and added to the graph. + ********************************************************************/ + +int gp_AddExtension(graphP theGraph, + int *pModuleID, + void *context, + void *(*dupContext)(void *, void *), + void (*freeContext)(void *), + graphFunctionTableP functions) +{ + graphExtensionP newExtension = NULL; + + if (theGraph == NULL || pModuleID == NULL || + context == NULL || dupContext == NULL || freeContext == NULL || + functions == NULL) + { + return NOTOK; + } + + // If the extension already exists, then don't redefine it. + if (gp_FindExtension(theGraph, *pModuleID, NULL) == TRUE) + { + return NOTOK; + } + + // Assign a unique ID to the extension if it does not already have one + if (*pModuleID == 0) + { + *pModuleID = ++moduleIDGenerator; + } + + // Allocate the new extension + if ((newExtension = (graphExtensionP)malloc(sizeof(graphExtension))) == NULL) + { + return NOTOK; + } + + // Assign the data payload of the extension + newExtension->moduleID = *pModuleID; + newExtension->context = context; + newExtension->dupContext = dupContext; + newExtension->freeContext = freeContext; + newExtension->functions = functions; + + _OverloadFunctions(theGraph, functions); + + // Make the new linkages + newExtension->next = (struct graphExtension *)theGraph->extensions; + theGraph->extensions = newExtension; + + // The new extension was successfully added + return OK; +} + +/******************************************************************** + _OverloadFunctions() + For each non-NULL function pointer, the pointer becomes the new value + for the function in the graph, and the old function pointer in the graph + is placed in the overload table. + + This way, when an extension function is invoked, it can choose to invoke + the base function before or after whatever extension behavior it provides. + + Also, when it comes time to remove an extension, this extension system + has access to the overload tables of all extensions so that it can unhook + the functions of the module being removed from the chains of calls for + each overloaded function. This will involve some pointer changes in + the overload tables of extensions other than the one being removed. + ********************************************************************/ + +void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions) +{ + void **graphFunctionTable = (void **)&theGraph->functions; + void **newFunctionTable = (void **)functions; + int numFunctions = sizeof(theGraph->functions) / sizeof(void *); + int K; + + for (K = 0; K < numFunctions; K++) + { + if (newFunctionTable[K] != NULL) + { + void *fp = graphFunctionTable[K]; + graphFunctionTable[K] = newFunctionTable[K]; + newFunctionTable[K] = fp; + } + } +} + +/******************************************************************** + gp_FindExtension() + + @param theGraph - the graph whose extension list is to be searched + @param moduleID - the identifier of the module whose extension context is desired + @param pContext - the return parameter that receives the value of the + extension, if found. This may be NULL if the extension was + not found or if the extension context value was NULL. + @return TRUE if the extension was found, NOTOK if not found + If FALSE is returned, then the context returned is guaranteed to be NULL + If TRUE is returned, the context returned may be NULL if that is the + current value of the module extension + ********************************************************************/ + +int gp_FindExtension(graphP theGraph, int moduleID, void **pContext) +{ + graphExtensionP first = NULL, next = NULL; + + if (pContext != NULL) + { + *pContext = NULL; + } + + if (theGraph == NULL || moduleID == 0) + { + return FALSE; + } + + first = theGraph->extensions; + + while (first != NULL) + { + next = (graphExtensionP)first->next; + if (first->moduleID == moduleID) + { + if (pContext != NULL) + { + *pContext = first->context; + } + return TRUE; + } + first = next; + } + + return FALSE; +} + +/******************************************************************** + gp_GetExtension() + + Calling this function is equivalent to invoking gp_FindExtension() + except that some debuggers have difficulty stepping into a function + that (properly) start by setting a local variable pointer to NULL + when the debugger has watch expressions that dereference a pointer + of the same name. In such cases, + + MyContext *context = NULL; + gp_FindExtension(theGraph, MYEXTENSION_ID, &context); + + can be replaced by + + MyContext *context = gp_GetExtension(theGraph, MYEXTENSION_ID); + + @param theGraph - the graph whose extension list is to be searched + @param moduleID - the identifier of the module whose extension context is desired + @return void pointer to the extension if found, or NULL if not found. + ********************************************************************/ +void *gp_GetExtension(graphP theGraph, int moduleID) +{ + void *context = NULL; + int result = gp_FindExtension(theGraph, moduleID, &context); + return result ? context : NULL; +} + +/******************************************************************** + gp_RemoveExtension() + @param theGraph - the graph from which to remove an extension + @param moduleID - the ID of the module whose extension context is to be removed + @return OK if the module is successfully removed or not in the list + NOTOK for internal errors, such as invalid parameters + ********************************************************************/ +int gp_RemoveExtension(graphP theGraph, int moduleID) +{ + graphExtensionP prev, curr, next; + + if (theGraph == NULL || moduleID == 0) + return NOTOK; + + prev = NULL; + curr = theGraph->extensions; + + while (curr != NULL) + { + next = (graphExtensionP)curr->next; + + if (curr->moduleID == moduleID) + break; + + prev = curr; + curr = next; + } + + // An extension can only be removed if it is found. Otherwise, + // we return OK because the extension degenerately removed + // (since it is already gone) + if (curr != NULL) + { + _FixupFunctionTables(theGraph, curr); + + // Unhook the curr extension + if (prev != NULL) + prev->next = (struct graphExtension *)next; + else + theGraph->extensions = next; + + // Free the curr extension + _FreeExtension(curr); + } + + return OK; +} + +/******************************************************************** + _FixupFunctionTables() + + Removes the functions in the curr function table from the function + call lists established by the function tables of all extensions and + theGraph. + + Since new extensions are prepended, extensions before curr may + have further overloaded the functions in the curr function table. + + For a non-NULL function pointer in the curr table, if there is + a preceding extension with the same function pointer non-NULL, then + the function table of the closest such preceding extension points + to the original overload function of the curr extension, and the + curr extension contains the pointer to the base function behavior, + so now the function table of that preceding extension must be changed + to the function pointer value in the curr extension. + ********************************************************************/ + +void _FixupFunctionTables(graphP theGraph, graphExtensionP curr) +{ + void **currFunctionTable = (void **)(curr->functions); + int numFunctions = sizeof(*(curr->functions)) / sizeof(void *); + int K; + + for (K = 0; K < numFunctions; K++) + { + if (currFunctionTable[K] != NULL) + { + void **nearestOverloadFunctionTable = (void **)&theGraph->functions; + graphExtensionP pred = _FindNearestOverload(theGraph, curr, K); + + if (pred != NULL) + nearestOverloadFunctionTable = (void **)pred->functions; + + nearestOverloadFunctionTable[K] = currFunctionTable[K]; + } + } +} + +/******************************************************************** + _FindNearestOverload() + ********************************************************************/ + +graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex) +{ + graphExtensionP curr = theGraph->extensions; + graphExtensionP found = NULL; + void **functionTable; + + while (curr != target) + { + functionTable = (void **)curr->functions; + if (functionTable[functionIndex] != NULL) + found = curr; + + curr = (graphExtensionP)curr->next; + } + + return found; +} + +/******************************************************************** + gp_CopyExtensions() + ********************************************************************/ + +int gp_CopyExtensions(graphP dstGraph, graphP srcGraph) +{ + graphExtensionP next = NULL, newNext = NULL, newLast = NULL; + + if (srcGraph == NULL || dstGraph == NULL) + return NOTOK; + + gp_FreeExtensions(dstGraph); + + next = srcGraph->extensions; + + while (next != NULL) + { + if ((newNext = (graphExtensionP)malloc(sizeof(graphExtension))) == NULL) + { + gp_FreeExtensions(dstGraph); + return NOTOK; + } + + newNext->moduleID = next->moduleID; + newNext->context = next->dupContext(next->context, dstGraph); + newNext->dupContext = next->dupContext; + newNext->freeContext = next->freeContext; + newNext->functions = next->functions; + newNext->next = NULL; + + if (newLast != NULL) + newLast->next = (struct graphExtension *)newNext; + else + dstGraph->extensions = newNext; + + newLast = newNext; + next = (graphExtensionP)next->next; + } + + return OK; +} + +/******************************************************************** + gp_FreeExtensions() + + @param pFirst - pointer to head pointer of graph extension list + + Each graph extension is freed, including invoking the freeContext + function provided when the extension was added. + ********************************************************************/ + +void gp_FreeExtensions(graphP theGraph) +{ + if (theGraph != NULL) + { + graphExtensionP curr = theGraph->extensions; + graphExtensionP next = NULL; + + while (curr != NULL) + { + next = (graphExtensionP)curr->next; + _FreeExtension(curr); + curr = next; + } + + theGraph->extensions = NULL; + _InitFunctionTable(theGraph); + } +} + +/******************************************************************** + _FreeExtension() + ********************************************************************/ +void _FreeExtension(graphExtensionP extension) +{ + if (extension->context != NULL && extension->freeContext != NULL) + { + extension->freeContext(extension->context); + } + free(extension); +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.h new file mode 100644 index 000000000..21dc72020 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.h @@ -0,0 +1,37 @@ +#ifndef GRAPH_EXTENSIONS_H +#define GRAPH_EXTENSIONS_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../graphStructures.h" + + int gp_AddExtension(graphP theGraph, + int *pModuleID, + void *context, + void *(*dupContext)(void *, void *), + void (*freeContext)(void *), + graphFunctionTableP overloadTable); + + int gp_FindExtension(graphP theGraph, int moduleID, void **pContext); + void *gp_GetExtension(graphP theGraph, int moduleID); + + int gp_RemoveExtension(graphP theGraph, int moduleID); + + int gp_CopyExtensions(graphP dstGraph, graphP srcGraph); + + void gp_FreeExtensions(graphP theGraph); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.private.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.private.h new file mode 100644 index 000000000..dc1e89dde --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.private.h @@ -0,0 +1,35 @@ +#ifndef GRAPH_EXTENSIONS_PRIVATE_H +#define GRAPH_EXTENSIONS_PRIVATE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "graphFunctionTable.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + int moduleID; + void *context; + void *(*dupContext)(void *, void *); + void (*freeContext)(void *); + + graphFunctionTableP functions; + + struct graphExtension *next; + } graphExtension; + + typedef graphExtension *graphExtensionP; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphFunctionTable.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphFunctionTable.h new file mode 100644 index 000000000..bbc37df39 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphFunctionTable.h @@ -0,0 +1,66 @@ +#ifndef GRAPHFUNCTIONTABLE_H +#define GRAPHFUNCTIONTABLE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* + NOTE: If you add any FUNCTION POINTERS to this function table, then you must + also initialize them in _InitFunctionTable() in graphUtils.c. + */ + typedef struct baseGraphStructure baseGraphStructure; + typedef baseGraphStructure *graphP; + + typedef struct + { + // These function pointers allow extension modules to overload some of + // the behaviors of protected functions. Only advanced applications + // will overload these functions + int (*fpEmbeddingInitialize)(graphP); + void (*fpEmbedBackEdgeToDescendant)(graphP, int, int, int, int); + void (*fpWalkUp)(graphP, int, int); + int (*fpWalkDown)(graphP, int, int); + int (*fpMergeBicomps)(graphP, int, int, int, int); + void (*fpMergeVertex)(graphP, int, int, int); + int (*fpHandleInactiveVertex)(graphP, int, int *, int *); + int (*fpHandleBlockedBicomp)(graphP, int, int, int); + int (*fpEmbedPostprocess)(graphP, int, int); + int (*fpMarkDFSPath)(graphP, int, int); + + int (*fpCheckEmbeddingIntegrity)(graphP, graphP); + int (*fpCheckObstructionIntegrity)(graphP, graphP); + + // These function pointers allow extension modules to overload some + // of the behaviors of gp_* function in the public API + int (*fpInitGraph)(graphP, int); + void (*fpReinitializeGraph)(graphP); + int (*fpEnsureArcCapacity)(graphP, int); + int (*fpSortVertices)(graphP); + + int (*fpReadPostprocess)(graphP, char *); + int (*fpWritePostprocess)(graphP, char **); + + void (*fpHideEdge)(graphP, int); + void (*fpRestoreEdge)(graphP, int); + int (*fpHideVertex)(graphP, int); + int (*fpRestoreVertex)(graphP); + int (*fpContractEdge)(graphP, int); + int (*fpIdentifyVertices)(graphP, int, int, int); + + } graphFunctionTable; + + typedef graphFunctionTable *graphFunctionTableP; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graph.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graph.h new file mode 100644 index 000000000..ab2f5c0d9 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graph.h @@ -0,0 +1,141 @@ +#ifndef GRAPH_H +#define GRAPH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "graphStructures.h" + +#include "io/g6-read-iterator.h" +#include "io/g6-write-iterator.h" +#include "io/strbuf.h" +#include "io/strOrFile.h" + +#include "extensionSystem/graphExtensions.h" + + /////////////////////////////////////////////////////////////////////////////// + // Definitions for higher-order operations at the vertex, edge and graph levels + /////////////////////////////////////////////////////////////////////////////// + + graphP gp_New(void); + + int gp_InitGraph(graphP theGraph, int N); + void gp_ReinitializeGraph(graphP theGraph); + int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph); + int gp_CopyGraph(graphP dstGraph, graphP srcGraph); + graphP gp_DupGraph(graphP theGraph); + + int gp_CreateRandomGraph(graphP theGraph); + int gp_CreateRandomGraphEx(graphP theGraph, int numEdges); + + void gp_Free(graphP *pGraph); + + int gp_Read(graphP theGraph, char *FileName); + int gp_ReadFromString(graphP theGraph, char *inputStr); + +#define WRITE_ADJLIST 1 +#define WRITE_ADJMATRIX 2 +#define WRITE_DEBUGINFO 3 +#define WRITE_G6 4 + + int gp_Write(graphP theGraph, char *FileName, int Mode); + int gp_WriteToString(graphP theGraph, char **pOutputStr, int Mode); + + int gp_IsNeighbor(graphP theGraph, int u, int v); + int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v); + int gp_GetVertexDegree(graphP theGraph, int v); + int gp_GetVertexInDegree(graphP theGraph, int v); + int gp_GetVertexOutDegree(graphP theGraph, int v); + + int gp_GetArcCapacity(graphP theGraph); + int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); + + int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink); + int gp_DynamicAddEdge(graphP theGraph, int u, int ulink, int v, int vlink); + int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink, + int v, int e_v, int e_vlink); + + void gp_HideEdge(graphP theGraph, int e); + void gp_RestoreEdge(graphP theGraph, int e); + int gp_HideVertex(graphP theGraph, int vertex); + int gp_DeleteEdge(graphP theGraph, int e, int nextLink); + + int gp_ContractEdge(graphP theGraph, int e); + int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore); + int gp_RestoreVertices(graphP theGraph); + + int gp_CreateDFSTree(graphP theGraph); + int gp_SortVertices(graphP theGraph); + int gp_LowpointAndLeastAncestor(graphP theGraph); + int gp_PreprocessForEmbedding(graphP theGraph); + + int gp_Embed(graphP theGraph, int embedFlags); + int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult); + + /* Possible Flags for gp_Embed. The planar and outerplanar settings are supported + natively. The rest require extension modules. */ + +#define EMBEDFLAGS_PLANAR 1 +#define EMBEDFLAGS_OUTERPLANAR 2 + +#define EMBEDFLAGS_DRAWPLANAR (4 | EMBEDFLAGS_PLANAR) + +#define EMBEDFLAGS_SEARCHFORK23 (16 | EMBEDFLAGS_OUTERPLANAR) +#define EMBEDFLAGS_SEARCHFORK4 (32 | EMBEDFLAGS_OUTERPLANAR) +#define EMBEDFLAGS_SEARCHFORK33 (64 | EMBEDFLAGS_PLANAR) + +#define EMBEDFLAGS_SEARCHFORK5 (128 | EMBEDFLAGS_PLANAR) + +#define EMBEDFLAGS_MAXIMALPLANARSUBGRAPH 256 +#define EMBEDFLAGS_PROJECTIVEPLANAR 512 +#define EMBEDFLAGS_TOROIDAL 1024 + +/* If LOGGING is defined, then write to the log, otherwise no-op + By default, neither release nor DEBUG builds including LOGGING. + Logging is useful for seeing details of how various algorithms + handle a particular graph. */ + +// #define LOGGING +#ifdef LOGGING + +#define gp_LogLine _LogLine +#define gp_Log _Log + + void _LogLine(char *Line); + void _Log(char *Line); + +#define gp_MakeLogStr1 _MakeLogStr1 +#define gp_MakeLogStr2 _MakeLogStr2 +#define gp_MakeLogStr3 _MakeLogStr3 +#define gp_MakeLogStr4 _MakeLogStr4 +#define gp_MakeLogStr5 _MakeLogStr5 + + char *_MakeLogStr1(char *format, int); + char *_MakeLogStr2(char *format, int, int); + char *_MakeLogStr3(char *format, int, int, int); + char *_MakeLogStr4(char *format, int, int, int, int); + char *_MakeLogStr5(char *format, int, int, int, int, int); + +#else +#define gp_LogLine(Line) +#define gp_Log(Line) +#define gp_MakeLogStr1(format, one) +#define gp_MakeLogStr2(format, one, two) +#define gp_MakeLogStr3(format, one, two, three) +#define gp_MakeLogStr4(format, one, two, three, four) +#define gp_MakeLogStr5(format, one, two, three, four, five) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c new file mode 100644 index 000000000..69d824363 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c @@ -0,0 +1,457 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define GRAPHDFSUTILS_C + +#include "graph.h" + +extern void _ClearVertexVisitedFlags(graphP theGraph, int); + +/******************************************************************** + gp_CreateDFSTree + Assigns Depth First Index (DFI) to each vertex. Also records parent + of each vertex in the DFS tree, and marks DFS tree edges that go from + parent to child. Forward arc cycle edges are also distinguished from + edges leading from a DFS tree descendant to an ancestor-- both DFS tree + edges and back arcs. The forward arcs are moved to the end of the + adjacency list to make the set easier to find and process. + + NOTE: This is a utility function provided for general use of the graph + library. The core planarity algorithm uses its own DFS in order to build + up related data structures at the same time as the DFS tree is created. + ********************************************************************/ + +#include "lowLevelUtils/platformTime.h" + +int gp_CreateDFSTree(graphP theGraph) +{ + stackP theStack; + int DFI, v, uparent, u, e; + +#ifdef PROFILE + platform_time start, end; + platform_GetTime(start); +#endif + + if (theGraph == NULL) + return NOTOK; + if (theGraph->internalFlags & FLAGS_DFSNUMBERED) + return OK; + + gp_LogLine("\ngraphDFSUtils.c/gp_CreateDFSTree() start"); + + theStack = theGraph->theStack; + + /* There are 2M edge records (arcs) and for each we can push 2 integers, + so a stack of 2 * arcCapacity integers suffices. + This is already in theGraph structure, so we make sure it's empty, + then clear all visited flags in prep for the Depth first search. */ + + if (sp_GetCapacity(theStack) < 2 * gp_GetArcCapacity(theGraph)) + return NOTOK; + + sp_ClearStack(theStack); + + _ClearVertexVisitedFlags(theGraph, FALSE); + + /* This outer loop causes the connected subgraphs of a disconnected + graph to be numbered */ + + for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++) + { + if (gp_IsNotDFSTreeRoot(theGraph, v)) + continue; + + sp_Push2(theStack, NIL, NIL); + while (sp_NonEmpty(theStack)) + { + sp_Pop2(theStack, uparent, e); + u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e); + + if (!gp_GetVertexVisited(theGraph, u)) + { + gp_LogLine(gp_MakeLogStr3("V=%d, DFI=%d, Parent=%d", u, DFI, uparent)); + + gp_SetVertexVisited(theGraph, u); + gp_SetVertexIndex(theGraph, u, DFI++); + gp_SetVertexParent(theGraph, u, uparent); + if (gp_IsArc(e)) + { + gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD); + gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT); + } + + /* Push edges to all unvisited neighbors. These will be either + tree edges to children or forward arcs of back edges */ + + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) + sp_Push2(theStack, u, e); + e = gp_GetNextArc(theGraph, e); + } + } + else + { + // If the edge leads to a visited vertex, then it is + // the forward arc of a back edge. + gp_SetEdgeType(theGraph, e, EDGE_TYPE_FORWARD); + gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_BACK); + } + } + } + + gp_LogLine("graphDFSUtils.c/gp_CreateDFSTree() end\n"); + + theGraph->internalFlags |= FLAGS_DFSNUMBERED; + +#ifdef PROFILE + platform_GetTime(end); + printf("DFS in %.3lf seconds.\n", platform_GetDuration(start, end)); +#endif + + return OK; +} + +/******************************************************************** + gp_SortVertices() + Once depth first numbering has been applied to the graph, the index + member of each vertex contains the DFI. This routine can reorder the + vertices in linear time so that they appear in ascending order by DFI. + Note that the index field is then used to store the original number + of the vertex. Therefore, a second call to this method will put the + vertices back to the original order and put the DFIs back into the + index fields of the vertices. + + NOTE: This function is used by the core planarity algorithm, once its + custom DFS has assigned DFIs to the vertices. Once gp_Embed() has + finished creating an embedding or obstructing subgraph, this function + can be called to restore the original vertex numbering, if needed. + ********************************************************************/ + +int gp_SortVertices(graphP theGraph) +{ + return theGraph->functions.fpSortVertices(theGraph); +} + +int _SortVertices(graphP theGraph) +{ + int v, EsizeOccupied, e, srcPos, dstPos; + +#ifdef PROFILE + platform_time start, end; + platform_GetTime(start); +#endif + + if (theGraph == NULL) + return NOTOK; + if (!(theGraph->internalFlags & FLAGS_DFSNUMBERED)) + if (gp_CreateDFSTree(theGraph) != OK) + return NOTOK; + + gp_LogLine("\ngraphDFSUtils.c/_SortVertices() start"); + + /* Change labels of edges from v to DFI(v)-- or vice versa + Also, if any links go back to locations 0 to n-1, then they + need to be changed because we are reordering the vertices */ + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e += 2) + { + if (gp_EdgeInUse(theGraph, e)) + { + gp_SetNeighbor(theGraph, e, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e))); + gp_SetNeighbor(theGraph, e + 1, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e + 1))); + } + } + + /* Convert DFSParent from v to DFI(v) or vice versa */ + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (gp_IsNotDFSTreeRoot(theGraph, v)) + gp_SetVertexParent(theGraph, v, gp_GetVertexIndex(theGraph, gp_GetVertexParent(theGraph, v))); + + /* Sort by 'v using constant time random access. Move each vertex to its + destination 'v', and store its source location in 'v'. */ + + /* First we clear the visitation flags. We need these to help mark + visited vertices because we change the 'v' field to be the source + location, so we cannot use index==v as a test for whether the + correct vertex is in location 'index'. */ + + _ClearVertexVisitedFlags(theGraph, FALSE); + + /* We visit each vertex location, skipping those marked as visited since + we've already moved the correct vertex into that location. The + inner loop swaps the vertex at location v into the correct position, + given by the index of the vertex at location v. Then it marks that + location as visited, then sets its index to be the location from + whence we obtained the vertex record. */ + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + srcPos = v; + while (!gp_GetVertexVisited(theGraph, v)) + { + dstPos = gp_GetVertexIndex(theGraph, v); + + gp_SwapVertexRec(theGraph, dstPos, theGraph, v); + gp_SwapVertexInfo(theGraph, dstPos, theGraph, v); + + gp_SetVertexVisited(theGraph, dstPos); + gp_SetVertexIndex(theGraph, dstPos, srcPos); + + srcPos = dstPos; + } + } + + /* Invert the bit that records the sort order of the graph */ + + theGraph->internalFlags ^= FLAGS_SORTEDBYDFI; + + gp_LogLine("graphDFSUtils.c/_SortVertices() end\n"); + +#ifdef PROFILE + platform_GetTime(end); + printf("SortVertices in %.3lf seconds.\n", platform_GetDuration(start, end)); +#endif + + return OK; +} + +/******************************************************************** + gp_LowpointAndLeastAncestor() + leastAncestor(v): min(v, ancestor neighbors of v, excluding parent) + Lowpoint(v): min(leastAncestor(v), Lowpoint of DFS children of v) + + Lowpoint is computed via a post-order traversal of the DFS tree. + We push the root of the DFS tree, then we loop while the stack is not empty. + We pop a vertex; if it is not marked, then we are on our way down the DFS + tree, so we mark it and push it back on, followed by pushing its + DFS children. The next time we pop the node, all of its children + will have been popped, marked+children pushed, and popped again. On + the second pop of the vertex, we can therefore compute the lowpoint + values based on the childrens' lowpoints and the least ancestor from + among the edges in the vertex's adjacency list. + + If they have not already been performed, gp_CreateDFSTree() and + gp_SortVertices() are invoked on the graph, and it is left in the + sorted state on completion of this method. + + NOTE: This is a utility function provided for general use of the graph + library. The core planarity algorithm computes leastAncestor during its + initial DFS, and it computes the lowpoint of a vertex as it embeds the + tree edges to its children. + ********************************************************************/ + +int gp_LowpointAndLeastAncestor(graphP theGraph) +{ + stackP theStack = theGraph->theStack; + int v, u, uneighbor, e, L, leastAncestor; + + if (theGraph == NULL) + return NOTOK; + + if (!(theGraph->internalFlags & FLAGS_DFSNUMBERED)) + if (gp_CreateDFSTree(theGraph) != OK) + return NOTOK; + + if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI)) + if (gp_SortVertices(theGraph) != OK) + return NOTOK; + +#ifdef PROFILE + platform_time start, end; + platform_GetTime(start); +#endif + + gp_LogLine("\ngraphDFSUtils.c/gp_LowpointAndLeastAncestor() start"); + + // A stack of size N suffices because at maximum every vertex is pushed only once + // However, since a larger stack is needed for the main DFS, this is mainly documentation + if (sp_GetCapacity(theStack) < theGraph->N) + return NOTOK; + + sp_ClearStack(theStack); + + _ClearVertexVisitedFlags(theGraph, FALSE); + + // This outer loop causes the connected subgraphs of a disconnected graph to be processed + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);) + { + if (gp_GetVertexVisited(theGraph, v)) + { + ++v; + continue; + } + + sp_Push(theStack, v); + while (sp_NonEmpty(theStack)) + { + sp_Pop(theStack, u); + + // If not visited, then we're on the pre-order visitation, so push u and its DFS children + if (!gp_GetVertexVisited(theGraph, u)) + { + // Mark u as visited, then push it back on the stack + gp_SetVertexVisited(theGraph, u); + ++v; + sp_Push(theStack, u); + + // Push the DFS children of u + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + { + sp_Push(theStack, gp_GetNeighbor(theGraph, e)); + } + + e = gp_GetNextArc(theGraph, e); + } + } + + // If u has been visited before, then this is the post-order visitation + else + { + // Start with high values because we are doing a min function + leastAncestor = L = u; + + // Compute leastAncestor and L, the least lowpoint from the DFS children + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + uneighbor = gp_GetNeighbor(theGraph, e); + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + { + if (L > gp_GetVertexLowpoint(theGraph, uneighbor)) + L = gp_GetVertexLowpoint(theGraph, uneighbor); + } + else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) + { + if (leastAncestor > uneighbor) + leastAncestor = uneighbor; + } + + e = gp_GetNextArc(theGraph, e); + } + + /* Assign leastAncestor and Lowpoint to the vertex */ + gp_SetVertexLeastAncestor(theGraph, u, leastAncestor); + gp_SetVertexLowpoint(theGraph, u, leastAncestor < L ? leastAncestor : L); + } + } + } + + gp_LogLine("graphDFSUtils.c/gp_LowpointAndLeastAncestor() end\n"); + +#ifdef PROFILE + platform_GetTime(end); + printf("Lowpoint in %.3lf seconds.\n", platform_GetDuration(start, end)); +#endif + + return OK; +} + +/******************************************************************** + gp_LeastAncestor() + + By simple pre-order visitation, compute the least ancestor of each + vertex that is directly adjacent to the vertex by a back edge. + + If they have not already been performed, gp_CreateDFSTree() and + gp_SortVertices() are invoked on the graph, and it is left in the + sorted state on completion of this method. + + NOTE: This method is not called by gp_LowpointAndLeastAncestor(), + which computes both values at the same time. + + NOTE: This method is useful in core planarity initialization when + a graph has already been DFS numbered and sorted by DFI. For example, + this allows the core planarity embedder to avoid perturbing unit test + graphs that may be designed and stored in a DFI sorted format. + ********************************************************************/ + +int gp_LeastAncestor(graphP theGraph) +{ + stackP theStack = theGraph->theStack; + int v, u, uneighbor, e, leastAncestor; + + if (theGraph == NULL) + return NOTOK; + + if (!(theGraph->internalFlags & FLAGS_DFSNUMBERED)) + if (gp_CreateDFSTree(theGraph) != OK) + return NOTOK; + + if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI)) + if (gp_SortVertices(theGraph) != OK) + return NOTOK; + +#ifdef PROFILE + platform_time start, end; + platform_GetTime(start); +#endif + + gp_LogLine("\ngraphDFSUtils.c/gp_LeastAncestor() start"); + + // A stack of size N suffices because at maximum every vertex is pushed only once + if (sp_GetCapacity(theStack) < theGraph->N) + return NOTOK; + + sp_ClearStack(theStack); + + // This outer loop causes the connected subgraphs of a disconnected graph to be processed + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);) + { + if (gp_GetVertexVisited(theGraph, v)) + { + ++v; + continue; + } + + sp_Push(theStack, v); + while (sp_NonEmpty(theStack)) + { + sp_Pop(theStack, u); + + if (!gp_GetVertexVisited(theGraph, u)) + { + gp_SetVertexVisited(theGraph, u); + ++v; + leastAncestor = u; + + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + uneighbor = gp_GetNeighbor(theGraph, e); + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + { + sp_Push(theStack, uneighbor); + } + else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) + { + if (leastAncestor > uneighbor) + leastAncestor = uneighbor; + } + + e = gp_GetNextArc(theGraph, e); + } + gp_SetVertexLeastAncestor(theGraph, u, leastAncestor); + } + } + } + + gp_LogLine("graphDFSUtils.c/gp_LeastAncestor() end\n"); + +#ifdef PROFILE + platform_GetTime(end); + printf("LeastAncestor in %.3lf seconds.\n", platform_GetDuration(start, end)); +#endif + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h new file mode 100644 index 000000000..275c964e3 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h @@ -0,0 +1,59 @@ +#ifndef GRAPHLIB_H +#define GRAPHLIB_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include + +#include "graph.h" + +#include "lowLevelUtils/platformTime.h" + +#include "homeomorphSearch/graphK23Search.h" +#include "homeomorphSearch/graphK33Search.h" +#include "homeomorphSearch/graphK4Search.h" +#include "planarityRelated/graphDrawPlanar.h" + + // This is the main location for the project and shared library version numbering. + // Changes here must be mirrored in configure.ac + // + // The overall project version numbering format is major.minor.maintenance.tweak + // Major is for an overhaul (e.g. many features, data structure change, change of backward compatibility) + // Minor is for feature addition (e.g. a new algorithm implementation added, new interface) + // Maintenance is for functional revision (e.g. bug fix to existing algorithm implementation) + // Tweak is for a non-functional revision (e.g. change of build scripts or testing code, user-facing string changes) + +#define GP_PROJECTVERSION_MAJOR 4 +#define GP_PROJECTVERSION_MINOR 0 +#define GP_PROJECTVERSION_MAINT 0 +#define GP_PROJECTVERSION_TWEAK 0 + + char *gp_GetProjectVersionFull(void); + +// Any change to the project version numbers should also affect the +// shared library version numbers below. +// +// See configure.ac for how to update these version numbers +#define GP_LIBPLANARITYVERSION_CURRENT 2 +#define GP_LIBPLANARITYVERSION_REVISION 0 +#define GP_LIBPLANARITYVERSION_AGE 0 + + char *gp_GetLibPlanarityVersionFull(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphStructures.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphStructures.h new file mode 100644 index 000000000..eb70277a8 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphStructures.h @@ -0,0 +1,788 @@ +#ifndef GRAPHSTRUCTURE_H +#define GRAPHSTRUCTURE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "lowLevelUtils/appconst.h" +#include "lowLevelUtils/listcoll.h" +#include "lowLevelUtils/stack.h" + +#include "extensionSystem/graphExtensions.private.h" +#include "extensionSystem/graphFunctionTable.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +// A return value to indicate success prior to completely processing a graph, whereas +// OK signifies EMBEDDABLE (no unreducible obstructions) and NOTOK signifies an exception. +#define NONEMBEDDABLE -1 + +// The initial setting for the edge storage capacity expressed as a constant factor of N, +// which is the number of vertices in the graph. By default, array E is allocated enough +// space to contain 3N edges, which is 6N arcs (half edges), but this initial setting +// can be overridden using gp_EnsureArcCapacity(), which is especially efficient if done +// before calling gp_InitGraph() or gp_Read(). +#define DEFAULT_EDGE_LIMIT 3 + + /******************************************************************** + Edge Record Definition + + An edge is defined by a pair of edge records, or arcs, allocated in + array E of a graph. An edge record represents the edge in the + adjacency list of each vertex to which the edge is incident. + + link[2]: the next and previous edge records (arcs) in the adjacency + list that contains this edge record. + + v: The vertex neighbor of the vertex whose adjacency list contains + this edge record (an index into array V). + + flags: Bits 0-15 reserved for library; bits 16 and higher for apps + Bit 0: Visited + Bit 1: DFS type has been set, versus not set + Bit 2: DFS tree edge, versus cycle edge (co-tree edge, etc.) + Bit 3: DFS arc to descendant, versus arc to ancestor + Bit 4: Inverted (same as marking an edge with a "sign" of -1) + Bit 5: Arc is directed into the containing vertex only + Bit 6: Arc is directed from the containing vertex only + ********************************************************************/ + + typedef struct + { + int link[2]; + int neighbor; + unsigned flags; + } edgeRec; + + typedef edgeRec *edgeRecP; + +#ifdef USE_FASTER_1BASEDARRAYS +#define gp_IsArc(e) (e) +#define gp_IsNotArc(e) (!(e)) +#define gp_GetFirstEdge(theGraph) (2) +#else +#define gp_IsArc(e) ((e) != NIL) +#define gp_IsNotArc(e) ((e) == NIL) +#define gp_GetFirstEdge(theGraph) (0) +#endif + +#define gp_EdgeInUse(theGraph, e) (gp_IsVertex(gp_GetNeighbor(theGraph, e))) +#define gp_EdgeNotInUse(theGraph, e) (gp_IsNotVertex(gp_GetNeighbor(theGraph, e))) +#define gp_EdgeIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (theGraph)->arcCapacity) +#define gp_EdgeInUseIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (((theGraph)->M + sp_GetCurrentSize((theGraph)->edgeHoles)) << 1)) + +// An edge is represented by two consecutive edge records (arcs) in the edge array E. +// If an even number, xor 1 will add one; if an odd number, xor 1 will subtract 1 +#define gp_GetTwinArc(theGraph, Arc) ((Arc) ^ 1) + +// Access to adjacency list pointers +#define gp_GetNextArc(theGraph, e) (theGraph->E[e].link[0]) +#define gp_GetPrevArc(theGraph, e) (theGraph->E[e].link[1]) +#define gp_GetAdjacentArc(theGraph, e, theLink) (theGraph->E[e].link[theLink]) + +#define gp_SetNextArc(theGraph, e, newNextArc) (theGraph->E[e].link[0] = newNextArc) +#define gp_SetPrevArc(theGraph, e, newPrevArc) (theGraph->E[e].link[1] = newPrevArc) +#define gp_SetAdjacentArc(theGraph, e, theLink, newArc) (theGraph->E[e].link[theLink] = newArc) + +// Access to vertex 'neighbor' member indicated by arc +#define gp_GetNeighbor(theGraph, e) (theGraph->E[e].neighbor) +#define gp_SetNeighbor(theGraph, e, v) (theGraph->E[e].neighbor = v) + +// Initializer for edge flags +#define gp_InitEdgeFlags(theGraph, e) (theGraph->E[e].flags = 0) + +// Definitions of and access to edge flags +#define EDGE_VISITED_MASK 1 +#define gp_GetEdgeVisited(theGraph, e) (theGraph->E[e].flags & EDGE_VISITED_MASK) +#define gp_ClearEdgeVisited(theGraph, e) (theGraph->E[e].flags &= ~EDGE_VISITED_MASK) +#define gp_SetEdgeVisited(theGraph, e) (theGraph->E[e].flags |= EDGE_VISITED_MASK) + +// The edge type is defined by bits 1-3, 2+4+8=14 +#define EDGE_TYPE_MASK 14 + +// Call gp_GetEdgeType(), then compare to one of these four possibilities +// EDGE_TYPE_CHILD - edge record is an arc to a DFS child +// EDGE_TYPE_FORWARD - edge record is an arc to a DFS descendant, not a DFS child +// EDGE_TYPE_PARENT - edge record is an arc to the DFS parent +// EDGE_TYPE_BACK - edge record is an arc to a DFS ancestor, not the DFS parent +#define EDGE_TYPE_CHILD 14 +#define EDGE_TYPE_FORWARD 10 +#define EDGE_TYPE_PARENT 6 +#define EDGE_TYPE_BACK 2 + +// EDGE_TYPE_NOTDEFINED - the edge record type has not been defined +// EDGE_TYPE_RANDOMTREE - edge record is part of a randomly generated tree +#define EDGE_TYPE_NOTDEFINED 0 +#define EDGE_TYPE_RANDOMTREE 4 + +#define gp_GetEdgeType(theGraph, e) (theGraph->E[e].flags & EDGE_TYPE_MASK) +#define gp_ClearEdgeType(theGraph, e) (theGraph->E[e].flags &= ~EDGE_TYPE_MASK) +#define gp_SetEdgeType(theGraph, e, type) (theGraph->E[e].flags |= type) +#define gp_ResetEdgeType(theGraph, e, type) \ + (theGraph->E[e].flags = (theGraph->E[e].flags & ~EDGE_TYPE_MASK) | type) + +#define EDGEFLAG_INVERTED_MASK 16 +#define gp_GetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_INVERTED_MASK) +#define gp_SetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags |= EDGEFLAG_INVERTED_MASK) +#define gp_ClearEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags &= (~EDGEFLAG_INVERTED_MASK)) +#define gp_XorEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags ^= EDGEFLAG_INVERTED_MASK) + +#define EDGEFLAG_DIRECTION_INONLY 32 +#define EDGEFLAG_DIRECTION_OUTONLY 64 +#define EDGEFLAG_DIRECTION_MASK 96 + +// Returns the direction, if any, of the edge record +#define gp_GetDirection(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_DIRECTION_MASK) + +// A direction of 0 clears directedness. Otherwise, edge record e is set +// to edgeFlag_Direction and e's twin arc is set to the opposing setting. +#define gp_SetDirection(theGraph, e, edgeFlag_Direction) \ + { \ + if (edgeFlag_Direction == EDGEFLAG_DIRECTION_INONLY) \ + { \ + theGraph->E[e].flags |= EDGEFLAG_DIRECTION_INONLY; \ + theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_OUTONLY; \ + } \ + else if (edgeFlag_Direction == EDGEFLAG_DIRECTION_OUTONLY) \ + { \ + theGraph->E[e].flags |= EDGEFLAG_DIRECTION_OUTONLY; \ + theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_INONLY; \ + } \ + else \ + { \ + theGraph->E[e].flags &= ~(EDGEFLAG_DIRECTION_INONLY | EDGEFLAG_DIRECTION_OUTONLY); \ + theGraph->E[gp_GetTwinArc(theGraph, e)].flags &= ~EDGEFLAG_DIRECTION_MASK; \ + } \ + } + +#define gp_CopyEdgeRec(dstGraph, edst, srcGraph, esrc) (dstGraph->E[edst] = srcGraph->E[esrc]) + + /******************************************************************** + Vertex Record Definition + + This record definition provides the data members needed for the + core structural information for both vertices and virtual vertices. + Vertices are also equipped with additional information provided by + the vertexInfo structure. + + The vertices of a graph are stored in the first N locations of array V. + Virtual vertices are secondary vertices used to help represent the + main vertices in substructural components of a graph (e.g. biconnected + components). + + link[2]: the first and last edge records (arcs) in the adjacency list + of the vertex. + + index: In vertices, stores either the depth first index of a vertex or + the original array index of the vertex if the vertices of the + graph are sorted by DFI. + In virtual vertices, the index may be used to indicate the vertex + that the virtual vertex represents, unless an algorithm has some + other way of making the association (for example, the planarity + algorithms rely on biconnected components and therefore place + virtual vertices of a vertex at positions corresponding to the + DFS children of the vertex). + + flags: Bits 0-15 reserved for library; bits 16 and higher for apps + Bit 0: visited, for vertices and virtual vertices + Use in lieu of TYPE_VERTEX_VISITED in K4 algorithm + Bit 1: Obstruction type VERTEX_TYPE_SET (versus not set, i.e. VERTEX_TYPE_UNKNOWN) + Bit 2: Obstruction type qualifier RYW (set) versus RXW (clear) + Bit 3: Obstruction type qualifier high (set) versus low (clear) + ********************************************************************/ + + typedef struct + { + int link[2]; + int index; + unsigned flags; + } vertexRec; + + typedef vertexRec *vertexRecP; + +// Accessors for vertex adjacency list links +#define gp_GetFirstArc(theGraph, v) (theGraph->V[v].link[0]) +#define gp_GetLastArc(theGraph, v) (theGraph->V[v].link[1]) +#define gp_GetArc(theGraph, v, theLink) (theGraph->V[v].link[theLink]) + +#define gp_SetFirstArc(theGraph, v, newFirstArc) (theGraph->V[v].link[0] = newFirstArc) +#define gp_SetLastArc(theGraph, v, newLastArc) (theGraph->V[v].link[1] = newLastArc) +#define gp_SetArc(theGraph, v, theLink, newArc) (theGraph->V[v].link[theLink] = newArc) + +// Vertex conversions and iteration +#ifdef USE_FASTER_1BASEDARRAYS +#define gp_IsVertex(v) (v) +#define gp_IsNotVertex(v) (!(v)) + +#define gp_GetFirstVertex(theGraph) (1) +#define gp_GetLastVertex(theGraph) ((theGraph)->N) +#define gp_VertexInRange(theGraph, v) ((v) <= (theGraph)->N) +#define gp_VertexInRangeDescending(theGraph, v) (v) + +#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N) +#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N) + +#define gp_IsVirtualVertex(theGraph, v) ((v) > theGraph->N) +#define gp_IsNotVirtualVertex(theGraph, v) ((v) <= theGraph->N) +#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex))) +#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex))) +#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N + 1) +#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV) +#define gp_VirtualVertexInRange(theGraph, v) ((v) <= theGraph->N + theGraph->NV) +#else +#define gp_IsVertex(v) ((v) != NIL) +#define gp_IsNotVertex(v) ((v) == NIL) + +#define gp_GetFirstVertex(theGraph) (0) +#define gp_GetLastVertex(theGraph) ((theGraph)->N - 1) +#define gp_VertexInRange(theGraph, v) ((v) < (theGraph)->N) +#define gp_VertexInRangeDescending(theGraph, v) ((v) >= 0) + +#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N) +#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N) + +#define gp_IsVirtualVertex(theGraph, v) ((v) >= theGraph->N) +#define gp_IsNotVirtualVertex(theGraph, v) ((v) < theGraph->N) +#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex))) +#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex))) +#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N) +#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV - 1) +#define gp_VirtualVertexInRange(theGraph, v) ((v) < theGraph->N + theGraph->NV) +#endif + +#define gp_GetRootFromDFSChild(theGraph, c) ((c) + theGraph->N) +#define gp_GetDFSChildFromRoot(theGraph, R) ((R) - theGraph->N) +#define gp_GetPrimaryVertexFromRoot(theGraph, R) gp_GetVertexParent(theGraph, gp_GetDFSChildFromRoot(theGraph, R)) + +#define gp_IsSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild))) +#define gp_IsNotSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexNotInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild))) + +#define gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(gp_GetVertexParent(theGraph, v)) +#define gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(gp_GetVertexParent(theGraph, v)) + +// Accessors for vertex index +#define gp_GetVertexIndex(theGraph, v) (theGraph->V[v].index) +#define gp_SetVertexIndex(theGraph, v, theIndex) (theGraph->V[v].index = theIndex) + +// Initializer for vertex flags +#define gp_InitVertexFlags(theGraph, v) (theGraph->V[v].flags = 0) + +// Definitions and accessors for vertex flags +#define VERTEX_VISITED_MASK 1 +#define gp_GetVertexVisited(theGraph, v) (theGraph->V[v].flags & VERTEX_VISITED_MASK) +#define gp_ClearVertexVisited(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_VISITED_MASK) +#define gp_SetVertexVisited(theGraph, v) (theGraph->V[v].flags |= VERTEX_VISITED_MASK) + +// The obstruction type is defined by bits 1-3, 2+4+8=14 +// Bit 1 - 2 if type set, 0 if not +// Bit 2 - 4 if Y side, 0 if X side +// Bit 3 - 8 if high, 0 if low +#define VERTEX_OBSTRUCTIONTYPE_MASK 14 + +// Call gp_GetVertexObstructionType, then compare to one of these four possibilities +// VERTEX_OBSTRUCTIONTYPE_HIGH_RXW - On the external face path between vertices R and X +// VERTEX_OBSTRUCTIONTYPE_LOW_RXW - X or on the external face path between vertices X and W +// VERTEX_OBSTRUCTIONTYPE_HIGH_RYW - On the external face path between vertices R and Y +// VERTEX_OBSTRUCTIONTYPE_LOW_RYW - Y or on the external face path between vertices Y and W +// VERTEX_OBSTRUCTIONTYPE_UNKNOWN - corresponds to all three bits off +#define VERTEX_OBSTRUCTIONTYPE_HIGH_RXW 10 +#define VERTEX_OBSTRUCTIONTYPE_LOW_RXW 2 +#define VERTEX_OBSTRUCTIONTYPE_HIGH_RYW 14 +#define VERTEX_OBSTRUCTIONTYPE_LOW_RYW 6 +#define VERTEX_OBSTRUCTIONTYPE_UNKNOWN 0 + +#define VERTEX_OBSTRUCTIONTYPE_MARKED 2 +#define VERTEX_OBSTRUCTIONTYPE_UNMARKED 0 + +#define gp_GetVertexObstructionType(theGraph, v) (theGraph->V[v].flags & VERTEX_OBSTRUCTIONTYPE_MASK) +#define gp_ClearVertexObstructionType(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_OBSTRUCTIONTYPE_MASK) +#define gp_SetVertexObstructionType(theGraph, v, type) (theGraph->V[v].flags |= type) +#define gp_ResetVertexObstructionType(theGraph, v, type) \ + (theGraph->V[v].flags = (theGraph->V[v].flags & ~VERTEX_OBSTRUCTIONTYPE_MASK) | type) + +#define gp_CopyVertexRec(dstGraph, vdst, srcGraph, vsrc) (dstGraph->V[vdst] = srcGraph->V[vsrc]) + +#define gp_SwapVertexRec(dstGraph, vdst, srcGraph, vsrc) \ + { \ + vertexRec tempV = dstGraph->V[vdst]; \ + dstGraph->V[vdst] = srcGraph->V[vsrc]; \ + srcGraph->V[vsrc] = tempV; \ + } + + /******************************************************************** + This structure defines a pair of links used by each vertex and virtual vertex + to create "short circuit" paths that eliminate unimportant vertices from + the external face, enabling more efficient traversal of the external face. + + It is also possible to embed the "short circuit" edges, but this approach + creates a better separation of concerns, imparts greater clarity, and + removes exceptionalities for handling additional fake "short circuit" edges. + + vertex[2]: The two adjacent vertices along the external face, possibly + short-circuiting paths of inactive vertices. + */ + + typedef struct + { + int vertex[2]; + } extFaceLinkRec; + + typedef extFaceLinkRec *extFaceLinkRecP; + +#define gp_GetExtFaceVertex(theGraph, v, link) (theGraph->extFace[v].vertex[link]) +#define gp_SetExtFaceVertex(theGraph, v, link, theVertex) (theGraph->extFace[v].vertex[link] = theVertex) + + /******************************************************************** + Vertex Info Structure Definition. + + This structure equips the primary (non-virtual) vertices with additional + information needed for lowpoint and planarity-related algorithms. + + parent: The DFI of the DFS tree parent of this vertex + leastAncestor: min(DFI of neighbors connected by backedge) + lowpoint: min(leastAncestor, min(lowpoint of DFS Children)) + + visitedInfo: enables algorithms to manage vertex visitation with more than + just a flag. For example, the planarity test flags visitation + as a step number that implicitly resets on each step, whereas + part of the planar drawing method signifies a first visitation + by storing the index of the first edge used to reach a vertex + pertinentEdge: Used by the planarity method; during Walkup, each vertex + that is directly adjacent via a back edge to the vertex v + currently being embedded will have the forward edge's index + stored in this field. During Walkdown, each vertex for which + this field is set will cause a back edge to be embedded. + Implicitly resets at each vertex step of the planarity method + pertinentRootsList: used by Walkup to store a list of child bicomp roots of + a vertex descendant of the current vertex that are pertinent + and must be merged by the Walkdown in order to embed the cycle + edges of the current vertex. Future pertinent child bicomp roots + are placed at the end of the list to ensure bicomps that are + only pertinent are processed first. + futurePertinentChild: indicates a DFS child with a lowpoint less than the + current vertex v. This member is initialized to the start of + the sortedDFSChildList and is advanced in a relaxed manner as + needed until one with a lowpoint less than v is found or until + there are no more children. + sortedDFSChildList: at the start of embedding, the list of DFS children of + this vertex is calculated in ascending order by DFI (sorted in + linear time). The list is used during Walkdown processing of + a vertex to process all of its children. It is also used in + future pertinence management when processing the ancestors of + the vertex. When a child C is merged into the same bicomp as + the vertex, it is removed from the list. + fwdArcList: at the start of embedding, the "back" edges from a vertex to + its DFS *descendants* (i.e. the forward arcs of the back edges) + are separated from the main adjacency list and placed in a + circular list until they are embedded. The list is sorted in + ascending DFI order of the descendants (in linear time). + This member indicates a node in that list. + */ + + typedef struct + { + int parent, leastAncestor, lowpoint; + + int visitedInfo; + + int pertinentEdge, + pertinentRoots, + futurePertinentChild, + sortedDFSChildList, + fwdArcList; + } vertexInfo; + + typedef vertexInfo *vertexInfoP; + +#define gp_GetVertexVisitedInfo(theGraph, v) (theGraph->VI[v].visitedInfo) +#define gp_SetVertexVisitedInfo(theGraph, v, theVisitedInfo) (theGraph->VI[v].visitedInfo = theVisitedInfo) + +#define gp_GetVertexParent(theGraph, v) (theGraph->VI[v].parent) +#define gp_SetVertexParent(theGraph, v, theParent) (theGraph->VI[v].parent = theParent) + +#define gp_GetVertexLeastAncestor(theGraph, v) (theGraph->VI[v].leastAncestor) +#define gp_SetVertexLeastAncestor(theGraph, v, theLeastAncestor) (theGraph->VI[v].leastAncestor = theLeastAncestor) + +#define gp_GetVertexLowpoint(theGraph, v) (theGraph->VI[v].lowpoint) +#define gp_SetVertexLowpoint(theGraph, v, theLowpoint) (theGraph->VI[v].lowpoint = theLowpoint) + +#define gp_GetVertexPertinentEdge(theGraph, v) (theGraph->VI[v].pertinentEdge) +#define gp_SetVertexPertinentEdge(theGraph, v, e) (theGraph->VI[v].pertinentEdge = e) + +#define gp_GetVertexPertinentRootsList(theGraph, v) (theGraph->VI[v].pertinentRoots) +#define gp_SetVertexPertinentRootsList(theGraph, v, pertinentRootsHead) (theGraph->VI[v].pertinentRoots = pertinentRootsHead) + +#define gp_GetVertexFirstPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, theGraph->VI[v].pertinentRoots) +#define gp_GetVertexFirstPertinentRootChild(theGraph, v) (theGraph->VI[v].pertinentRoots) +#define gp_GetVertexLastPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL)) +#define gp_GetVertexLastPertinentRootChild(theGraph, v) LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL) + +#define gp_DeleteVertexPertinentRoot(theGraph, v, R) \ + gp_SetVertexPertinentRootsList(theGraph, v, \ + LCDelete(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) + +#define gp_PrependVertexPertinentRoot(theGraph, v, R) \ + gp_SetVertexPertinentRootsList(theGraph, v, \ + LCPrepend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) + +#define gp_AppendVertexPertinentRoot(theGraph, v, R) \ + gp_SetVertexPertinentRootsList(theGraph, v, \ + LCAppend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) + +#define gp_GetVertexFuturePertinentChild(theGraph, v) (theGraph->VI[v].futurePertinentChild) +#define gp_SetVertexFuturePertinentChild(theGraph, v, theFuturePertinentChild) (theGraph->VI[v].futurePertinentChild = theFuturePertinentChild) + +// Used to advance futurePertinentChild of w to the next separated DFS child with a lowpoint less than v +// Once futurePertinentChild advances past a child, no future planarity operation could make that child +// relevant to future pertinence +#define gp_UpdateVertexFuturePertinentChild(theGraph, w, v) \ + while (gp_IsVertex(theGraph->VI[w].futurePertinentChild)) \ + { \ + /* Skip children that 1) aren't future pertinent, 2) have been merged into the bicomp with w */ \ + if (gp_GetVertexLowpoint(theGraph, theGraph->VI[w].futurePertinentChild) >= v || \ + gp_IsNotSeparatedDFSChild(theGraph, theGraph->VI[w].futurePertinentChild)) \ + { \ + theGraph->VI[w].futurePertinentChild = \ + gp_GetVertexNextDFSChild(theGraph, w, gp_GetVertexFuturePertinentChild(theGraph, w)); \ + } \ + else \ + break; \ + } + +#define gp_GetVertexSortedDFSChildList(theGraph, v) (theGraph->VI[v].sortedDFSChildList) +#define gp_SetVertexSortedDFSChildList(theGraph, v, theSortedDFSChildList) (theGraph->VI[v].sortedDFSChildList = theSortedDFSChildList) + +#define gp_GetVertexNextDFSChild(theGraph, v, c) LCGetNext(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c) + +#define gp_AppendDFSChild(theGraph, v, c) \ + LCAppend(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c) + +#define gp_GetVertexFwdArcList(theGraph, v) (theGraph->VI[v].fwdArcList) +#define gp_SetVertexFwdArcList(theGraph, v, theFwdArcList) (theGraph->VI[v].fwdArcList = theFwdArcList) + +#define gp_CopyVertexInfo(dstGraph, dstI, srcGraph, srcI) (dstGraph->VI[dstI] = srcGraph->VI[srcI]) + +#define gp_SwapVertexInfo(dstGraph, dstPos, srcGraph, srcPos) \ + { \ + vertexInfo tempVI = dstGraph->VI[dstPos]; \ + dstGraph->VI[dstPos] = srcGraph->VI[srcPos]; \ + srcGraph->VI[srcPos] = tempVI; \ + } + + /******************************************************************** + Variables needed in embedding by Kuratowski subgraph isolator: + minorType: the type of planarity obstruction found. + v: the current vertex being processed + r: the root of the bicomp on which the Walkdown failed + x,y: stopping vertices on bicomp rooted by r + w: pertinent vertex on ext. face path below x and y + px, py: attachment points of x-y path, + z: Unused except in minors D and E (not needed in A, B, C). + + ux,dx: endpoints of unembedded edge that helps connext x with + ancestor of v + uy,dy: endpoints of unembedded edge that helps connext y with + ancestor of v + dw: descendant endpoint in unembedded edge to v + uz,dz: endpoints of unembedded edge that helps connext z with + ancestor of v (for minors B and E, not A, C, D). + */ + + typedef struct + { + int minorType; + int v, r, x, y, w, px, py, z; + int ux, dx, uy, dy, dw, uz, dz; + } isolatorContext; + + typedef isolatorContext *isolatorContextP; + +#define MINORTYPE_A 1 +#define MINORTYPE_B 2 +#define MINORTYPE_C 4 +#define MINORTYPE_D 8 +#define MINORTYPE_E 16 +#define MINORTYPE_E1 32 +#define MINORTYPE_E2 64 +#define MINORTYPE_E3 128 +#define MINORTYPE_E4 256 + +#define MINORTYPE_E5 512 +#define MINORTYPE_E6 1024 +#define MINORTYPE_E7 2048 + + /******************************************************************** + Graph structure definition + V : Array of vertex records (allocated size N + NV) + VI: Array of additional vertexInfo structures (allocated size N) + N : Number of primary vertices (the "order" of the graph) + NV: Number of virtual vertices (currently always equal to N) + + E : Array of edge records (edge records come in pairs and represent half edges, or arcs) + M: Number of edges (the "size" of the graph) + arcCapacity: the maximum number of edge records allowed in E (the size of E) + edgeHoles: free locations in E where edges have been deleted + + theStack: Used by various graph routines needing a stack + internalFlags: Additional state information about the graph + embedFlags: controls type of embedding (e.g. planar) + + IC: contains additional useful variables for Kuratowski subgraph isolation. + BicompRootLists: storage space for pertinent bicomp root lists that develop + during embedding + sortedDFSChildLists: storage for the sorted DFS child lists of each vertex + extFace: Array of (N + NV) external face short circuit records + + extensions: a list of extension data structures + functions: a table of function pointers that can be overloaded to provide + extension behaviors to the graph + */ + + struct baseGraphStructure + { + vertexRecP V; + vertexInfoP VI; + int N, NV; + + edgeRecP E; + int M, arcCapacity; + stackP edgeHoles; + + stackP theStack; + int internalFlags, embedFlags; + + isolatorContext IC; + listCollectionP BicompRootLists, sortedDFSChildLists; + extFaceLinkRecP extFace; + + graphExtensionP extensions; + graphFunctionTable functions; + }; + + typedef struct baseGraphStructure baseGraphStructure; + typedef baseGraphStructure *graphP; + + /* Flags for graph: + FLAGS_DFSNUMBERED is set if DFSNumber() has succeeded for the graph + FLAGS_SORTEDBYDFI records whether the graph is in original vertex + order or sorted by depth first index. Successive calls to + SortVertices() toggle this bit. + FLAGS_OBSTRUCTIONFOUND is set by gp_Embed() if an embedding obstruction + was isolated in the graph returned. It is cleared by gp_Embed() + if an obstruction was not found. The flag is used by + gp_TestEmbedResultIntegrity() to decide what integrity tests to run. + FLAGS_ZEROBASEDIO is typically set by gp_Read() to indicate that the + adjacency list representation began with index 0. + */ + +#define FLAGS_DFSNUMBERED 1 +#define FLAGS_SORTEDBYDFI 2 +#define FLAGS_OBSTRUCTIONFOUND 4 +#define FLAGS_ZEROBASEDIO 8 + +/******************************************************************** + More link structure accessors/manipulators + ********************************************************************/ + +// Definitions that enable getting the next or previous arc +// as if the adjacency list were circular, i.e. that the +// first arc and last arc were linked +#define gp_GetNextArcCircular(theGraph, e) \ + (gp_IsArc(gp_GetNextArc(theGraph, e)) ? gp_GetNextArc(theGraph, e) : gp_GetFirstArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor)) + +#define gp_GetPrevArcCircular(theGraph, e) \ + (gp_IsArc(gp_GetPrevArc(theGraph, e)) ? gp_GetPrevArc(theGraph, e) : gp_GetLastArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor)) + +// Definitions that make the cross-link binding between a vertex and an arc +// The old first or last arc should be bound to this arc by separate calls, +// e.g. see gp_AttachFirstArc() and gp_AttachLastArc() +#define gp_BindFirstArc(theGraph, v, arc) \ + { \ + gp_SetPrevArc(theGraph, arc, NIL); \ + gp_SetFirstArc(theGraph, v, arc); \ + } + +#define gp_BindLastArc(theGraph, v, arc) \ + { \ + gp_SetNextArc(theGraph, arc, NIL); \ + gp_SetLastArc(theGraph, v, arc); \ + } + +// Attaches an arc between the current binding between a vertex and its first arc +#define gp_AttachFirstArc(theGraph, v, arc) \ + { \ + if (gp_IsArc(gp_GetFirstArc(theGraph, v))) \ + { \ + gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \ + gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \ + } \ + else \ + gp_BindLastArc(theGraph, v, arc); \ + gp_BindFirstArc(theGraph, v, arc); \ + } + +// Attaches an arc between the current binding betwen a vertex and its last arc +#define gp_AttachLastArc(theGraph, v, arc) \ + { \ + if (gp_IsArc(gp_GetLastArc(theGraph, v))) \ + { \ + gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \ + gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \ + } \ + else \ + gp_BindFirstArc(theGraph, v, arc); \ + gp_BindLastArc(theGraph, v, arc); \ + } + +// Moves an arc that is in the adjacency list of v to the start of the adjacency list +#define gp_MoveArcToFirst(theGraph, v, arc) \ + if (arc != gp_GetFirstArc(theGraph, v)) \ + { \ + /* If the arc is last in the adjacency list of uparent, \ + then we delete it by adjacency list end management */ \ + if (arc == gp_GetLastArc(theGraph, v)) \ + { \ + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), NIL); \ + gp_SetLastArc(theGraph, v, gp_GetPrevArc(theGraph, arc)); \ + } \ + /* Otherwise, we delete the arc from the middle of the list */ \ + else \ + { \ + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \ + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \ + } \ + \ + /* Now add arc e as the new first arc of uparent. \ + Note that the adjacency list is non-empty at this time */ \ + gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \ + gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \ + gp_BindFirstArc(theGraph, v, arc); \ + } + +// Moves an arc that is in the adjacency list of v to the end of the adjacency list +#define gp_MoveArcToLast(theGraph, v, arc) \ + if (arc != gp_GetLastArc(theGraph, v)) \ + { \ + /* If the arc is first in the adjacency list of vertex v, \ + then we delete it by adjacency list end management */ \ + if (arc == gp_GetFirstArc(theGraph, v)) \ + { \ + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), NIL); \ + gp_SetFirstArc(theGraph, v, gp_GetNextArc(theGraph, arc)); \ + } \ + /* Otherwise, we delete the arc from the middle of the list */ \ + else \ + { \ + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \ + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \ + } \ + \ + /* Now add the arc as the new last arc of v. \ + Note that the adjacency list is non-empty at this time */ \ + gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \ + gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \ + gp_BindLastArc(theGraph, v, arc); \ + } + + // Methods for attaching an arc into the adjacency list or detaching an arc from it. + // The terms AddArc, InsertArc and DeleteArc are not used because the arcs are not + // inserted or added to or deleted from storage (only whole edges are inserted or deleted) + void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc); + void gp_DetachArc(graphP theGraph, int arc); + + /******************************************************************** + PERTINENT() + A vertex is pertinent in a partially processed graph if there is an + unprocessed back edge between the vertex v whose edges are currently + being processed and either the vertex or a DFS descendant D of the + vertex not in the same bicomp as the vertex. + + The vertex is either directly adjacent to v by an unembedded back edge + or there is an unembedded back edge (v, D) and the vertex is a cut + vertex in the partially processed graph along the DFS tree path from + D to v. + + Pertinence is a dynamic property that can change for a vertex after + each edge addition. In other words, a vertex can become non-pertinent + during step v as more back edges to v are embedded. + ********************************************************************/ + +#define PERTINENT(theGraph, theVertex) \ + (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) || \ + gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex))) + +#define NOTPERTINENT(theGraph, theVertex) \ + (gp_IsNotArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) && \ + gp_IsNotVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex))) + + /******************************************************************** + FUTUREPERTINENT() + A vertex is future-pertinent in a partially processed graph if + there is an unprocessed back edge between a DFS ancestor A of the + vertex v whose edges are currently being processed and either + theVertex or a DFS descendant D of theVertex not in the same bicomp + as theVertex. + + Either theVertex is directly adjacent to A by an unembedded back edge + or there is an unembedded back edge (A, D) and theVertex is a cut + vertex in the partially processed graph along the DFS tree path from + D to A. + + If no more edges are added to the partially processed graph prior to + processing the edges of A, then the vertex would be pertinent. + The addition of edges to the partially processed graph can alter + both the pertinence and future pertinence of a vertex. For example, + if the vertex is pertinent due to an unprocessed back edge (v, D1) and + future pertinent due to an unprocessed back edge (A, D2), then the + vertex may lose both its pertinence and future pertinence when edge + (v, D1) is added if D2 is in the same subtree as D1. + + Generally, pertinence and future pertinence are dynamic properties + that can change for a vertex after each edge addition. + + Note that gp_UpdateVertexFuturePertinentChild() must be called before + this macro. Since it is a statement and not a void expression, the + desired commented out version does not compile (except with special + compiler extensions not assumed by this code). + ********************************************************************/ + +#define FUTUREPERTINENT(theGraph, theVertex, v) \ + (theGraph->VI[theVertex].leastAncestor < v || \ + (gp_IsVertex(theGraph->VI[theVertex].futurePertinentChild) && \ + theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v)) + +#define NOTFUTUREPERTINENT(theGraph, theVertex, v) \ + (theGraph->VI[theVertex].leastAncestor >= v && \ + (gp_IsNotVertex(theGraph->VI[theVertex].futurePertinentChild) || \ + theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint >= v)) + + // This is the definition that would be preferrable if a while loop could be a void expression + // #define FUTUREPERTINENT(theGraph, theVertex, v) + // ( theGraph->VI[theVertex].leastAncestor < v || + // ((gp_UpdateVertexFuturePertinentChild(theGraph, theVertex, v), + // gp_IsArc(theGraph->VI[theVertex].futurePertinentChild)) && + // theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) ) + + /******************************************************************** + INACTIVE() + For planarity algorithms, a vertex is inactive if it is neither pertinent + nor future pertinent. + ********************************************************************/ + +#define INACTIVE(theGraph, theVertex, v) \ + (NOTPERTINENT(theGraph, theVertex) && \ + NOTFUTUREPERTINENT(theGraph, theVertex, v)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c new file mode 100644 index 000000000..14f372031 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c @@ -0,0 +1,2563 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "graphLib.h" + +/* Imported functions for FUNCTION POINTERS */ + +extern int _EmbeddingInitialize(graphP theGraph); +extern int _SortVertices(graphP theGraph); +extern void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); +extern void _WalkUp(graphP theGraph, int v, int e); +extern int _WalkDown(graphP theGraph, int v, int RootVertex); +extern int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); +extern void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R); +extern int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); +extern int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); +extern int _MarkDFSPath(graphP theGraph, int ancestor, int descendant); +extern int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); +extern int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); +extern int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph); +extern int _ReadPostprocess(graphP theGraph, char *extraData); +extern int _WritePostprocess(graphP theGraph, char **pExtraData); + +/* Internal util functions for FUNCTION POINTERS */ + +int _HideVertex(graphP theGraph, int vertex); +void _HideEdge(graphP theGraph, int arcPos); +void _RestoreEdge(graphP theGraph, int arcPos); +int _ContractEdge(graphP theGraph, int e); +int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore); +int _RestoreVertex(graphP theGraph); + +/******************************************************************** + Private functions, except exported within library + ********************************************************************/ + +void _InitIsolatorContext(graphP theGraph); +void _ClearVisitedFlags(graphP theGraph); +void _ClearVertexVisitedFlags(graphP theGraph, int); +void _ClearEdgeVisitedFlags(graphP theGraph); +int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); +int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); +void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); +int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue); +int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); + +int _HideInternalEdges(graphP theGraph, int vertex); +int _RestoreInternalEdges(graphP theGraph, int stackBottom); +int _RestoreHiddenEdges(graphP theGraph, int stackBottom); + +int _GetBicompSize(graphP theGraph, int BicompRoot); +int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot); +int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot); + +void _InitFunctionTable(graphP theGraph); + +/******************************************************************** + Private functions. + ********************************************************************/ + +void _InitVertices(graphP theGraph); +void _InitEdges(graphP theGraph); + +void _ClearGraph(graphP theGraph); + +int _GetRandomNumber(int NMin, int NMax); + +/* Private functions for which there are FUNCTION POINTERS */ + +void _InitVertexRec(graphP theGraph, int v); +void _InitVertexInfo(graphP theGraph, int v); +void _InitEdgeRec(graphP theGraph, int e); + +int _InitGraph(graphP theGraph, int N); +void _ReinitializeGraph(graphP theGraph); +int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); + +/******************************************************************** + gp_GetProjectVersionFull() + Return full major.minor.maint.tweak version string for the graph planarity project + ********************************************************************/ + +char *gp_GetProjectVersionFull(void) +{ + static char projectVersionStr[MAXLINE + 1]; + sprintf(projectVersionStr, "%d.%d.%d.%d", + GP_PROJECTVERSION_MAJOR, + GP_PROJECTVERSION_MINOR, + GP_PROJECTVERSION_MAINT, + GP_PROJECTVERSION_TWEAK); + return projectVersionStr; +} + +/******************************************************************** + gp_GetLibPlanarityVersionFull() + Returns full current:revision:age version string for the graph planarity shared library + ********************************************************************/ + +char *gp_GetLibPlanarityVersionFull(void) +{ + static char libPlanarityVersionStr[MAXLINE + 1]; + sprintf(libPlanarityVersionStr, "%d:%d:%d", + GP_LIBPLANARITYVERSION_CURRENT, + GP_LIBPLANARITYVERSION_REVISION, + GP_LIBPLANARITYVERSION_AGE); + return libPlanarityVersionStr; +} + +/******************************************************************** + gp_New() + Constructor for graph object. + Can create two graphs if restricted to no dynamic memory. + ********************************************************************/ + +graphP gp_New(void) +{ + graphP theGraph = (graphP)malloc(sizeof(baseGraphStructure)); + + if (theGraph != NULL) + { + theGraph->E = NULL; + theGraph->V = NULL; + theGraph->VI = NULL; + + theGraph->BicompRootLists = NULL; + theGraph->sortedDFSChildLists = NULL; + theGraph->theStack = NULL; + + theGraph->extFace = NULL; + + theGraph->edgeHoles = NULL; + + theGraph->extensions = NULL; + + _InitFunctionTable(theGraph); + + _ClearGraph(theGraph); + } + + return theGraph; +} + +/******************************************************************** + _InitFunctionTable() + + If you add functions to the function table, then they must be + initialized here, but you must also add the new function pointer + to the definition of the graphFunctionTable in graphFunctionTable.h + + Function headers for the functions used to initialize the table are + classified at the top of this file as either imported from other + compilation units (extern) or private to this compilation unit. + Search for FUNCTION POINTERS in this file to see where to add the + function header. + ********************************************************************/ + +void _InitFunctionTable(graphP theGraph) +{ + theGraph->functions.fpEmbeddingInitialize = _EmbeddingInitialize; + theGraph->functions.fpEmbedBackEdgeToDescendant = _EmbedBackEdgeToDescendant; + theGraph->functions.fpWalkUp = _WalkUp; + theGraph->functions.fpWalkDown = _WalkDown; + theGraph->functions.fpMergeBicomps = _MergeBicomps; + theGraph->functions.fpMergeVertex = _MergeVertex; + theGraph->functions.fpHandleBlockedBicomp = _HandleBlockedBicomp; + theGraph->functions.fpHandleInactiveVertex = _HandleInactiveVertex; + theGraph->functions.fpEmbedPostprocess = _EmbedPostprocess; + theGraph->functions.fpMarkDFSPath = _MarkDFSPath; + theGraph->functions.fpCheckEmbeddingIntegrity = _CheckEmbeddingIntegrity; + theGraph->functions.fpCheckObstructionIntegrity = _CheckObstructionIntegrity; + + theGraph->functions.fpInitGraph = _InitGraph; + theGraph->functions.fpReinitializeGraph = _ReinitializeGraph; + theGraph->functions.fpEnsureArcCapacity = _EnsureArcCapacity; + theGraph->functions.fpSortVertices = _SortVertices; + + theGraph->functions.fpReadPostprocess = _ReadPostprocess; + theGraph->functions.fpWritePostprocess = _WritePostprocess; + + theGraph->functions.fpHideEdge = _HideEdge; + theGraph->functions.fpRestoreEdge = _RestoreEdge; + theGraph->functions.fpHideVertex = _HideVertex; + theGraph->functions.fpRestoreVertex = _RestoreVertex; + theGraph->functions.fpContractEdge = _ContractEdge; + theGraph->functions.fpIdentifyVertices = _IdentifyVertices; +} + +/******************************************************************** + gp_InitGraph() + Allocates memory for vertex and edge records now that N is known. + The arcCapacity is set to (2 * DEFAULT_EDGE_LIMIT * N) unless it + has already been set by gp_EnsureArcCapacity() + + For V, we need 2N vertex records, N for vertices and N for virtual vertices (root copies). + + For VI, we need N vertexInfo records. + + For E, we need arcCapacity edge records. + + The BicompRootLists and sortedDFSChildLists are of size N and start out empty. + + The stack, initially empty, is made big enough for a pair of integers + per edge record (2 * arcCapacity), or 6N integers if the arcCapacity + was set below the default value. + + The edgeHoles stack, initially empty, is set to arcCapacity / 2, + which is big enough to push every edge (to indicate an edge + you only need to indicate one of its two edge records) + + Returns OK on success, NOTOK on all failures. + On NOTOK, graph extensions are freed so that the graph is + returned to the post-condition of gp_New(). + ********************************************************************/ + +int gp_InitGraph(graphP theGraph, int N) +{ + // valid params check + if (theGraph == NULL || N <= 0) + return NOTOK; + + // Should not call init a second time; use reinit + if (theGraph->N) + return NOTOK; + + return theGraph->functions.fpInitGraph(theGraph, N); +} + +int _InitGraph(graphP theGraph, int N) +{ + int Vsize, VIsize, Esize, stackSize; + + // Compute the vertex and edge capacities of the graph + theGraph->N = N; + theGraph->NV = N; + theGraph->arcCapacity = theGraph->arcCapacity > 0 ? theGraph->arcCapacity : 2 * DEFAULT_EDGE_LIMIT * N; + VIsize = gp_PrimaryVertexIndexBound(theGraph); + Vsize = gp_VertexIndexBound(theGraph); + Esize = gp_EdgeIndexBound(theGraph); + + // Stack size is 2 integers per arc, or 6 integers per vertex in case of small arcCapacity + stackSize = 2 * Esize; + stackSize = stackSize < 6 * N ? 6 * N : stackSize; + + // Allocate memory as described above + if ((theGraph->V = (vertexRecP)calloc(Vsize, sizeof(vertexRec))) == NULL || + (theGraph->VI = (vertexInfoP)calloc(VIsize, sizeof(vertexInfo))) == NULL || + (theGraph->E = (edgeRecP)calloc(Esize, sizeof(edgeRec))) == NULL || + (theGraph->BicompRootLists = LCNew(VIsize)) == NULL || + (theGraph->sortedDFSChildLists = LCNew(VIsize)) == NULL || + (theGraph->theStack = sp_New(stackSize)) == NULL || + (theGraph->extFace = (extFaceLinkRecP)calloc(Vsize, sizeof(extFaceLinkRec))) == NULL || + (theGraph->edgeHoles = sp_New(Esize / 2)) == NULL || + 0) + { + _ClearGraph(theGraph); + return NOTOK; + } + + // Initialize memory + _InitVertices(theGraph); + _InitEdges(theGraph); + _InitIsolatorContext(theGraph); + + return OK; +} + +/******************************************************************** + _InitVertices() + ********************************************************************/ +void _InitVertices(graphP theGraph) +{ +#ifdef USE_FASTER_1BASEDARRAYS + memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec)); + memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo)); + memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); +#else + int v; + + memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec)); + memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo)); + memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + gp_InitVertexFlags(theGraph, v); +#endif + // N.B. This is the legacy API-based approach to initializing the vertices + // int v; + + // // Initialize primary vertices + // for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + // { + // _InitVertexRec(theGraph, v); + // _InitVertexInfo(theGraph, v); + // gp_SetExtFaceVertex(theGraph, v, 0, NIL); + // gp_SetExtFaceVertex(theGraph, v, 1, NIL); + // } + + // // Initialize virtual vertices + // for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) + // { + // _InitVertexRec(theGraph, v); + // gp_SetExtFaceVertex(theGraph, v, 0, NIL); + // gp_SetExtFaceVertex(theGraph, v, 1, NIL); + // } +} + +/******************************************************************** + _InitEdges() + ********************************************************************/ +void _InitEdges(graphP theGraph) +{ +#ifdef USE_FASTER_1BASEDARRAYS + memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); +#else + int e, Esize; + + memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); + + Esize = gp_EdgeIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) + gp_InitEdgeFlags(theGraph, e); +#endif + // N.B. This is the legacy API-based approach to initializing the edges + // int e, Esize; + + // Esize = gp_EdgeIndexBound(theGraph); + // for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) + // _InitEdgeRec(theGraph, e); +} + +/******************************************************************** + gp_ReinitializeGraph() + Reinitializes a graph, restoring it to the state it was in immediately + after gp_InitGraph() processed it. + ********************************************************************/ + +void gp_ReinitializeGraph(graphP theGraph) +{ + if (theGraph == NULL || theGraph->N <= 0) + return; + + theGraph->functions.fpReinitializeGraph(theGraph); +} + +void _ReinitializeGraph(graphP theGraph) +{ + theGraph->M = 0; + theGraph->internalFlags = theGraph->embedFlags = 0; + + _InitVertices(theGraph); + _InitEdges(theGraph); + _InitIsolatorContext(theGraph); + + LCReset(theGraph->BicompRootLists); + LCReset(theGraph->sortedDFSChildLists); + sp_ClearStack(theGraph->theStack); + sp_ClearStack(theGraph->edgeHoles); +} + +/******************************************************************** + gp_GetArcCapacity() + Returns the arcCapacity of theGraph, which is twice the maximum + number of edges that can be added to the theGraph. + ********************************************************************/ +int gp_GetArcCapacity(graphP theGraph) +{ + return theGraph->arcCapacity - gp_GetFirstEdge(theGraph); +} + +/******************************************************************** + gp_EnsureArcCapacity() + This method ensures that theGraph is or will be capable of storing + at least requiredArcCapacity edge records. Two edge records are + needed per edge. + + This method is most performant when invoked immediately after + gp_New(), since it must only set the arcCapacity and then let + normal initialization to occur through gp_InitGraph(). + + This method is also a constant time operation if the graph already + has at least the requiredArcCapacity, since it will return OK + without making any structural changes. + + This method is generally more performant if it is invoked before + attaching extensions to the graph. Some extensions associate + parallel data with edge records, which is a faster operation if + the associated data is created and initialized only after the + proper arcCapacity is specified. + + If the graph has been initialized and has a lower arc capacity, + then the array of edge records is reallocated to satisfy the + requiredArcCapacity. The new array contains the old edges and + edge holes at the same locations, and all newly created edge records + are initialized. + + Also, if the arc capacity must be increased, then the + arcCapacity member of theGraph is changed and both + theStack and edgeHoles are expanded (since the sizes of both + are based on the arc capacity). + + Extensions that add to data associated with edges must overload + this method to ensure capacity in the parallel extension data + structures. An extension can return NOTOK if it does not + support arc capacity expansion. The extension function will + not be called if arcCapacity is expanded before the graph is + initialized, and it is assumed that extensions will allocate + parallel data structures according to the arc capacity. + + If an extension supports arc capacity expansion, then higher + performance can be obtained by using the method of unhooking + the initializers for individual edge records before invoking + the superclass version of fpEnsureArcCapacity(). Ideally, + application authors should ensure the proper arc capacity before + attaching extensions to achieve better performance. + + Returns NOTOK on failure to reallocate the edge record array to + satisfy the requiredArcCapacity, or if the requested + capacity is odd + OK if reallocation is not required or if reallocation succeeds + ********************************************************************/ +int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) +{ + if (theGraph == NULL || requiredArcCapacity <= 0) + return NOTOK; + + // Train callers to only ask for an even number of arcs, since + // two are required per edge or directed edge. + if (requiredArcCapacity & 1) + return NOTOK; + + if (theGraph->arcCapacity >= requiredArcCapacity) + return OK; + + // In the special case where gp_InitGraph() has not yet been called, + // we can simply set the higher arcCapacity since normal initialization + // will then allocate the correct number of edge records. + if (theGraph->N == 0) + { + theGraph->arcCapacity = requiredArcCapacity; + return OK; + } + + // Try to expand the arc capacity + return theGraph->functions.fpEnsureArcCapacity(theGraph, requiredArcCapacity); +} + +int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) +{ + stackP newStack; + int e, Esize = gp_EdgeIndexBound(theGraph), + newEsize = gp_GetFirstEdge(theGraph) + requiredArcCapacity; + + // If the new size is less than or equal to the old size, then + // the graph already has the required arc capacity + if (newEsize <= Esize) + return OK; + + // Expand theStack + if (sp_GetCapacity(theGraph->theStack) < 2 * requiredArcCapacity) + { + int stackSize = 2 * requiredArcCapacity; + + if (stackSize < 6 * theGraph->N) + { + // NOTE: Since this routine only makes the stack bigger, this + // calculation is not needed here because we already ensured + // we had stack capacity of the greater of 2*arcs and 6*N + // But we do it for clarity and consistency (e.g. so this rule + // is not forgotten whenever a "SetArcCapacity" method or a + // "reduceArcCapacity" method is added) + stackSize = 6 * theGraph->N; + } + + if ((newStack = sp_New(stackSize)) == NULL) + return NOTOK; + + sp_CopyContent(newStack, theGraph->theStack); + sp_Free(&theGraph->theStack); + theGraph->theStack = newStack; + } + + // Expand edgeHoles + if ((newStack = sp_New(requiredArcCapacity / 2)) == NULL) + { + return NOTOK; + } + + sp_CopyContent(newStack, theGraph->edgeHoles); + sp_Free(&theGraph->edgeHoles); + theGraph->edgeHoles = newStack; + + // Reallocate the edgeRec array to the new size, + theGraph->E = (edgeRecP)realloc(theGraph->E, newEsize * sizeof(edgeRec)); + if (theGraph->E == NULL) + return NOTOK; + + // Initialize the new edge records + for (e = Esize; e < newEsize; e++) + _InitEdgeRec(theGraph, e); + + // The new arcCapacity has been successfully achieved + theGraph->arcCapacity = requiredArcCapacity; + return OK; +} + +/******************************************************************** + _InitVertexRec() + Sets the fields in a single vertex record to initial values + ********************************************************************/ + +void _InitVertexRec(graphP theGraph, int v) +{ + gp_SetFirstArc(theGraph, v, NIL); + gp_SetLastArc(theGraph, v, NIL); + gp_SetVertexIndex(theGraph, v, NIL); + gp_InitVertexFlags(theGraph, v); +} + +/******************************************************************** + _InitVertexInfo() + Sets the fields in a single vertex record to initial values + ********************************************************************/ + +void _InitVertexInfo(graphP theGraph, int v) +{ + gp_SetVertexParent(theGraph, v, NIL); + gp_SetVertexLeastAncestor(theGraph, v, NIL); + gp_SetVertexLowpoint(theGraph, v, NIL); + + gp_SetVertexVisitedInfo(theGraph, v, NIL); + gp_SetVertexPertinentEdge(theGraph, v, NIL); + gp_SetVertexPertinentRootsList(theGraph, v, NIL); + gp_SetVertexFuturePertinentChild(theGraph, v, NIL); + gp_SetVertexSortedDFSChildList(theGraph, v, NIL); + gp_SetVertexFwdArcList(theGraph, v, NIL); +} + +/******************************************************************** + _InitEdgeRec() + Sets the fields in a single edge record structure to initial values + ********************************************************************/ + +void _InitEdgeRec(graphP theGraph, int e) +{ + gp_SetNeighbor(theGraph, e, NIL); + gp_SetPrevArc(theGraph, e, NIL); + gp_SetNextArc(theGraph, e, NIL); + gp_InitEdgeFlags(theGraph, e); +} + +/******************************************************************** + _InitIsolatorContext() + ********************************************************************/ + +void _InitIsolatorContext(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + IC->minorType = 0; + IC->v = IC->r = IC->x = IC->y = IC->w = IC->px = IC->py = IC->z = + IC->ux = IC->dx = IC->uy = IC->dy = IC->dw = IC->uz = IC->dz = NIL; +} + +/******************************************************************** + _ClearVisitedFlags() + ********************************************************************/ + +void _ClearVisitedFlags(graphP theGraph) +{ + _ClearVertexVisitedFlags(theGraph, TRUE); + _ClearEdgeVisitedFlags(theGraph); +} + +/******************************************************************** + _ClearVertexVisitedFlags() + ********************************************************************/ + +void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices) +{ + int v; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + gp_ClearVertexVisited(theGraph, v); + + if (includeVirtualVertices) + for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) + gp_ClearVertexVisited(theGraph, v); +} + +/******************************************************************** + _ClearEdgeVisitedFlags() + ********************************************************************/ + +void _ClearEdgeVisitedFlags(graphP theGraph) +{ + int e, EsizeOccupied; + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) + gp_ClearEdgeVisited(theGraph, e); +} + +/******************************************************************** + _ClearVisitedFlagsInBicomp() + + Clears the visited flag of the vertices and arcs in the bicomp rooted + by BicompRoot. + + This method uses the stack but preserves whatever may have been + on it. In debug mode, it will return NOTOK if the stack overflows. + This method pushes at most one integer per vertex in the bicomp. + + Returns OK on success, NOTOK on implementation failure. + ********************************************************************/ + +int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot) +{ + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + int v, e; + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, v); + gp_ClearVertexVisited(theGraph, v); + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + gp_ClearEdgeVisited(theGraph, e); + + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetNextArc(theGraph, e); + } + } + return OK; +} + +/******************************************************************** + _ClearVisitedFlagsInOtherBicomps() + Typically, we want to clear all visited flags in the graph + (see _ClearVisitedFlags). However, in some algorithms this would be + too costly, so it is necessary to clear the visited flags only + in one bicomp (see _ClearVisitedFlagsInBicomp), then do some processing + that sets some of the flags then performs some tests. If the tests + are positive, then we can clear all the visited flags in the + other bicomps (the processing may have set the visited flags in the + one bicomp in a particular way that we want to retain, so we skip + the given bicomp). + ********************************************************************/ + +int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot) +{ + int R; + + for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) + { + if (R != BicompRoot && gp_VirtualVertexInUse(theGraph, R)) + { + if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + } + } + return OK; +} + +/******************************************************************** + _ClearVisitedFlagsInUnembeddedEdges() + Unembedded edges aren't part of any bicomp yet, but it may be + necessary to clear their visited flags. + ********************************************************************/ + +void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph) +{ + int v, e; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + e = gp_GetVertexFwdArcList(theGraph, v); + while (gp_IsArc(e)) + { + gp_ClearEdgeVisited(theGraph, e); + gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + + e = gp_GetNextArc(theGraph, e); + if (e == gp_GetVertexFwdArcList(theGraph, v)) + e = NIL; + } + } +} + +/**************************************************************************** + _ClearVisitedFlagsOnPath() + This method clears the visited flags on the vertices and edges on the path + (u, v, ..., w, x) in which all vertices except the endpoints u and x + are degree 2. This method avoids performing more than constant work at the + path endpoints u and x, so the total work is on the order of the path length. + + Returns OK on success, NOTOK on internal failure + ****************************************************************************/ + +int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x) +{ + int e, eTwin; + + // We want to exit u from e, but we get eTwin first here in order to avoid + // work, in case the degree of u is greater than 2. + eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u); + if (gp_IsNotArc(eTwin)) + return NOTOK; + e = gp_GetTwinArc(theGraph, eTwin); + + v = u; + + do + { + // Mark the vertex and the exiting edge + gp_ClearVertexVisited(theGraph, v); + gp_ClearEdgeVisited(theGraph, e); + gp_ClearEdgeVisited(theGraph, eTwin); + + // Get the next vertex + v = gp_GetNeighbor(theGraph, e); + e = gp_GetNextArcCircular(theGraph, eTwin); + eTwin = gp_GetTwinArc(theGraph, e); + } while (v != x); + + // Mark the last vertex with 'visited' + gp_ClearVertexVisited(theGraph, x); + + return OK; +} + +/**************************************************************************** + _SetVisitedFlagsOnPath() + This method sets the visited flags on the vertices and edges on the path + (u, v, ..., w, x) in which all vertices except the endpoints u and x + are degree 2. This method avoids performing more than constant work at the + path endpoints u and x, so the total work is on the order of the path length. + + Returns OK on success, NOTOK on internal failure + ****************************************************************************/ + +int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x) +{ + int e, eTwin; + + // We want to exit u from e, but we get eTwin first here in order to avoid + // work, in case the degree of u is greater than 2. + eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u); + if (gp_IsNotArc(eTwin)) + return NOTOK; + e = gp_GetTwinArc(theGraph, eTwin); + + v = u; + + do + { + // Mark the vertex and the exiting edge + gp_SetVertexVisited(theGraph, v); + gp_SetEdgeVisited(theGraph, e); + gp_SetEdgeVisited(theGraph, eTwin); + + // Get the next vertex + v = gp_GetNeighbor(theGraph, e); + e = gp_GetNextArcCircular(theGraph, eTwin); + eTwin = gp_GetTwinArc(theGraph, e); + } while (v != x); + + // Mark the last vertex with 'visited' + gp_SetVertexVisited(theGraph, x); + + return OK; +} + +/******************************************************************** + _FillVertexVisitedInfoInBicomp() + + Places the FillValue into the visitedInfo of the non-virtual vertices + in the bicomp rooted by BicompRoot. + + This method uses the stack but preserves whatever may have been + on it. In debug mode, it will return NOTOK if the stack overflows. + This method pushes at most one integer per vertex in the bicomp. + + Returns OK on success, NOTOK on implementation failure. + ********************************************************************/ + +int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue) +{ + int v, e; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, v); + + if (gp_IsNotVirtualVertex(theGraph, v)) + gp_SetVertexVisitedInfo(theGraph, v, FillValue); + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetNextArc(theGraph, e); + } + } + return OK; +} + +/******************************************************************** + _ClearVertexTypeInBicomp() + + Clears the 'obstruction type' bits for each vertex in the bicomp + rooted by BicompRoot. + + This method uses the stack but preserves whatever may have been + on it. In debug mode, it will return NOTOK if the stack overflows. + This method pushes at most one integer per vertex in the bicomp. + + Returns OK on success, NOTOK on implementation failure. + ********************************************************************/ + +int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot) +{ + int V, e; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + gp_ClearVertexObstructionType(theGraph, V); + + e = gp_GetFirstArc(theGraph, V); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetNextArc(theGraph, e); + } + } + return OK; +} + +/******************************************************************** + _ClearGraph() + Clears all memory used by the graph, restoring it to the state it + was in immediately after gp_New() created it. + ********************************************************************/ + +void _ClearGraph(graphP theGraph) +{ + if (theGraph->V != NULL) + { + free(theGraph->V); + theGraph->V = NULL; + } + if (theGraph->VI != NULL) + { + free(theGraph->VI); + theGraph->V = NULL; + } + if (theGraph->E != NULL) + { + free(theGraph->E); + theGraph->E = NULL; + } + + theGraph->N = 0; + theGraph->NV = 0; + theGraph->M = 0; + theGraph->arcCapacity = 0; + theGraph->internalFlags = 0; + theGraph->embedFlags = 0; + + _InitIsolatorContext(theGraph); + + LCFree(&theGraph->BicompRootLists); + LCFree(&theGraph->sortedDFSChildLists); + + sp_Free(&theGraph->theStack); + + if (theGraph->extFace != NULL) + { + free(theGraph->extFace); + theGraph->extFace = NULL; + } + + sp_Free(&theGraph->edgeHoles); + + gp_FreeExtensions(theGraph); +} + +/******************************************************************** + gp_Free() + Frees G and V, then the graph record. Then sets your pointer to NULL + (so you must pass the address of your pointer). + ********************************************************************/ + +void gp_Free(graphP *pGraph) +{ + if (pGraph == NULL) + return; + if (*pGraph == NULL) + return; + + _ClearGraph(*pGraph); + + free(*pGraph); + *pGraph = NULL; +} + +/******************************************************************** + gp_CopyAdjacencyLists() + Copies the adjacency lists from the srcGraph to the dstGraph. + This method intentionally copies only the adjacency lists of the + first N vertices, so the adjacency lists of virtual vertices are + excluded (unless the caller temporarily resets the value of N to + include NV). + + Returns OK on success, NOTOK on failures, e.g. if the two graphs + have different orders N or if the arcCapacity of dstGraph cannot + be increased to match that of srcGraph. + ********************************************************************/ +int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph) +{ + int v, e, EsizeOccupied; + + if (dstGraph == NULL || srcGraph == NULL) + return NOTOK; + + if (dstGraph->N != srcGraph->N || dstGraph->N == 0) + return NOTOK; + + if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK) + return NOTOK; + + // Copy the links that hook each owning vertex to its adjacency list + for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++) + { + gp_SetFirstArc(dstGraph, v, gp_GetFirstArc(srcGraph, v)); + gp_SetLastArc(dstGraph, v, gp_GetLastArc(srcGraph, v)); + } + + // Copy the adjacency links and neighbor pointers for each arc + EsizeOccupied = gp_EdgeInUseIndexBound(srcGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) + { + gp_SetNeighbor(dstGraph, e, gp_GetNeighbor(srcGraph, e)); + gp_SetNextArc(dstGraph, e, gp_GetNextArc(srcGraph, e)); + gp_SetPrevArc(dstGraph, e, gp_GetPrevArc(srcGraph, e)); + } + + // Tell the dstGraph how many edges it now has and where the edge holes are + dstGraph->M = srcGraph->M; + sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles); + + return OK; +} + +/******************************************************************** + gp_CopyGraph() + Copies the content of the srcGraph into the dstGraph. The dstGraph + must have been previously initialized with the same number of + vertices as the srcGraph (e.g. gp_InitGraph(dstGraph, srcGraph->N). + + Returns OK for success, NOTOK for failure. + ********************************************************************/ + +int gp_CopyGraph(graphP dstGraph, graphP srcGraph) +{ + int v, e, Esize; + + // Parameter checks + if (dstGraph == NULL || srcGraph == NULL) + { + return NOTOK; + } + + // The graphs need to be the same order and initialized + if (dstGraph->N != srcGraph->N || dstGraph->N == 0) + { + return NOTOK; + } + + // Ensure dstGraph has the required arc capacity; this expands + // dstGraph if needed, but does not contract. An error is only + // returned if the expansion fails. + if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK) + { + return NOTOK; + } + + // Copy the primary vertices. Augmentations to vertices created + // by extensions are copied below by gp_CopyExtensions() + for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++) + { + gp_CopyVertexRec(dstGraph, v, srcGraph, v); + gp_CopyVertexInfo(dstGraph, v, srcGraph, v); + gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0)); + gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1)); + } + + // Copy the virtual vertices. Augmentations to virtual vertices created + // by extensions are copied below by gp_CopyExtensions() + for (v = gp_GetFirstVirtualVertex(srcGraph); gp_VirtualVertexInRange(srcGraph, v); v++) + { + gp_CopyVertexRec(dstGraph, v, srcGraph, v); + gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0)); + gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1)); + } + + // Copy the basic EdgeRec structures. Augmentations to the edgeRec structure + // created by extensions are copied below by gp_CopyExtensions() + Esize = gp_EdgeIndexBound(srcGraph); + for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) + gp_CopyEdgeRec(dstGraph, e, srcGraph, e); + + // Give the dstGraph the same size and intrinsic properties + dstGraph->N = srcGraph->N; + dstGraph->NV = srcGraph->NV; + dstGraph->M = srcGraph->M; + dstGraph->internalFlags = srcGraph->internalFlags; + dstGraph->embedFlags = srcGraph->embedFlags; + + dstGraph->IC = srcGraph->IC; + + LCCopy(dstGraph->BicompRootLists, srcGraph->BicompRootLists); + LCCopy(dstGraph->sortedDFSChildLists, srcGraph->sortedDFSChildLists); + sp_Copy(dstGraph->theStack, srcGraph->theStack); + sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles); + + // Copy the set of extensions, which includes copying the + // extension data as well as the function overload tables + if (gp_CopyExtensions(dstGraph, srcGraph) != OK) + return NOTOK; + + // Copy the graph's function table, which has the pointers to + // the most recent extension overloads of each function (or + // the original function pointer if a particular function has + // not been overloaded). + // This must be done after copying the extension because the + // first step of copying the extensions is to delete the + // dstGraph extensions, which clears its function table. + // Therefore, no good to assign the srcGraph functions *before* + // copying the extensions because the assignment would be wiped out + // This, in turn, means that the DupContext function of an extension + // *cannot* depend on any extension function overloads; the extension + // must directly invoke extension functions only. + dstGraph->functions = srcGraph->functions; + + return OK; +} + +/******************************************************************** + gp_DupGraph() + ********************************************************************/ + +graphP gp_DupGraph(graphP theGraph) +{ + graphP result; + + if ((result = gp_New()) == NULL) + return NULL; + + if (gp_InitGraph(result, theGraph->N) != OK || + gp_CopyGraph(result, theGraph) != OK) + { + gp_Free(&result); + return NULL; + } + + return result; +} + +/******************************************************************** + gp_CreateRandomGraph() + + Creates a randomly generated graph. First a tree is created by + connecting each vertex to some successor. Then a random number of + additional random edges are added. If an edge already exists, then + we retry until a non-existent edge is picked. + + This function assumes the caller has already called srand(). + + Returns OK on success, NOTOK on failure + ********************************************************************/ + +int gp_CreateRandomGraph(graphP theGraph) +{ + int N, M, u, v, m; + + N = theGraph->N; + + /* Generate a random tree; note that this method virtually guarantees + that the graph will be renumbered, but it is linear time. + Also, we are not generating the DFS tree but rather a tree + that simply ensures the resulting random graph is connected. */ + + for (v = gp_GetFirstVertex(theGraph) + 1; gp_VertexInRange(theGraph, v); v++) + { + u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v - 1); + if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) + return NOTOK; + } + + /* Generate a random number of additional edges + (actually, leave open a small chance that no + additional edges will be added). */ + + M = _GetRandomNumber(7 * N / 8, theGraph->arcCapacity / 2); + + if (M > N * (N - 1) / 2) + M = N * (N - 1) / 2; + + for (m = N - 1; m < M; m++) + { + u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph) - 1); + v = _GetRandomNumber(u + 1, gp_GetLastVertex(theGraph)); + + // If the edge (u,v) exists, decrement eIndex to try again + if (gp_IsNeighbor(theGraph, u, v)) + m--; + + // If the edge (u,v) doesn't exist, add it + else + { + if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + _GetRandomNumber() + This function generates a random number between NMin and NMax + inclusive. It assumes that the caller has called srand(). + It calls rand(), but before truncating to the proper range, + it adds the high bits of the rand() result into the low bits. + The result of this is that the randomness appearing in the + truncated bits also has an affect on the non-truncated bits. + ********************************************************************/ + +int _GetRandomNumber(int NMin, int NMax) +{ + int N = rand(); + + if (NMax < NMin) + return NMin; + + N += ((N & 0xFFFF0000) >> 16); + N += ((N & 0x0000FF00) >> 8); + N &= 0x7FFFFFF; + N %= (NMax - NMin + 1); + return N + NMin; +} + +/******************************************************************** + _getUnprocessedChild() + Support routine for gp_Create RandomGraphEx(), this function + obtains a child of the given vertex in the randomly generated + tree that has not yet been processed. NIL is returned if the + given vertex has no unprocessed children + + ********************************************************************/ + +int _getUnprocessedChild(graphP theGraph, int parent) +{ + int e = gp_GetFirstArc(theGraph, parent); + int eTwin = gp_GetTwinArc(theGraph, e); + int child = gp_GetNeighbor(theGraph, e); + + // The tree edges were added to the beginning of the adjacency list, + // and we move processed tree edge records to the end of the list, + // so if the immediate next arc (edge record) is not a tree edge + // then we return NIL because the vertex has no remaining + // unprocessed children + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED) + return NIL; + + // If the child has already been processed, then all children + // have been pushed to the end of the list, and we have just + // encountered the first child we processed, so there are no + // remaining unprocessed children */ + if (gp_GetEdgeVisited(theGraph, e)) + return NIL; + + // We have found an edge leading to an unprocessed child, so + // we mark it as processed so that it doesn't get returned + // again in future iterations. + gp_SetEdgeVisited(theGraph, e); + gp_SetEdgeVisited(theGraph, eTwin); + + // Now we move the edge record in the parent vertex to the end + // of the adjacency list of that vertex. + gp_MoveArcToLast(theGraph, parent, e); + + // Now we move the edge record in the child vertex to the + // end of the adjacency list of the child. + gp_MoveArcToLast(theGraph, child, eTwin); + + // Now we set the child's parent and return the child. + gp_SetVertexParent(theGraph, child, parent); + + return child; +} + +/******************************************************************** + _hasUnprocessedChild() + Support routine for gp_Create RandomGraphEx(), this function + obtains a child of the given vertex in the randomly generated + tree that has not yet been processed. False (0) is returned + unless the given vertex has an unprocessed child. + ********************************************************************/ + +int _hasUnprocessedChild(graphP theGraph, int parent) +{ + int e = gp_GetFirstArc(theGraph, parent); + + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED) + return 0; + + if (gp_GetEdgeVisited(theGraph, e)) + return 0; + + return 1; +} + +/******************************************************************** + gp_CreateRandomGraphEx() + Given a graph structure with a pre-specified number of vertices N, + this function creates a graph with the specified number of edges. + + If numEdges <= 3N-6, then the graph generated is planar. If + numEdges is larger, then a maximal planar graph is generated, then + (numEdges - 3N + 6) additional random edges are added. + + This function assumes the caller has already called srand(). + ********************************************************************/ + +int gp_CreateRandomGraphEx(graphP theGraph, int numEdges) +{ + int N, arc, M, root, v, c, p, last, u, e, EsizeOccupied; + + N = theGraph->N; + + if (numEdges > theGraph->arcCapacity / 2) + numEdges = theGraph->arcCapacity / 2; + + /* Generate a random tree. */ + + for (v = gp_GetFirstVertex(theGraph) + 1; gp_VertexInRange(theGraph, v); v++) + { + u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v - 1); + if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) + return NOTOK; + + else + { + arc = gp_GetNeighborEdgeRecord(theGraph, u, v); + gp_SetEdgeType(theGraph, arc, EDGE_TYPE_RANDOMTREE); + gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, arc), EDGE_TYPE_RANDOMTREE); + gp_ClearEdgeVisited(theGraph, arc); + gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, arc)); + } + } + + /* Add edges up to the limit or until the graph is maximal planar. */ + + M = numEdges <= 3 * N - 6 ? numEdges : 3 * N - 6; + + root = gp_GetFirstVertex(theGraph); + v = last = _getUnprocessedChild(theGraph, root); + + while (v != root && theGraph->M < M) + { + c = _getUnprocessedChild(theGraph, v); + + if (gp_IsVertex(c)) + { + if (last != v) + { + if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) + return NOTOK; + } + + if (gp_AddEdge(theGraph, root, 1, c, 1) != OK) + return NOTOK; + + v = last = c; + } + + else + { + p = gp_GetVertexParent(theGraph, v); + while (gp_IsVertex(p) && gp_IsNotVertex(c = _getUnprocessedChild(theGraph, p))) + { + v = p; + p = gp_GetVertexParent(theGraph, v); + if (gp_IsVertex(p) && p != root) + { + if (gp_AddEdge(theGraph, last, 1, p, 1) != OK) + return NOTOK; + } + } + + if (gp_IsVertex(p)) + { + if (p == root) + { + if (gp_AddEdge(theGraph, v, 1, c, 1) != OK) + return NOTOK; + + if (v != last) + { + if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) + return NOTOK; + } + } + else + { + if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) + return NOTOK; + } + + if (p != root) + { + if (gp_AddEdge(theGraph, root, 1, c, 1) != OK) + return NOTOK; + last = c; + } + + v = c; + } + } + } + + /* Add additional edges if the limit has not yet been reached. */ + + while (theGraph->M < numEdges) + { + u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)); + v = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)); + + if (u != v && !gp_IsNeighbor(theGraph, u, v)) + if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) + return NOTOK; + } + + /* Clear the edge types back to 'unknown' */ + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = 0; e < EsizeOccupied; e++) + { + gp_ClearEdgeType(theGraph, e); + gp_ClearEdgeVisited(theGraph, e); + } + + /* Put all DFSParent indicators back to NIL */ + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + gp_SetVertexParent(theGraph, v, NIL); + + return OK; +} + +/******************************************************************** + gp_IsNeighbor() + + Checks whether v is already in u's adjacency list, i.e. does the arc + u -> v exist. + If there is an edge record for v in u's list, but it is marked INONLY, + then it represents the arc v->u but not u->v, so it is ignored. + + Returns TRUE or FALSE. + ********************************************************************/ + +int gp_IsNeighbor(graphP theGraph, int u, int v) +{ + int e = gp_GetFirstArc(theGraph, u); + + while (gp_IsArc(e)) + { + if (gp_GetNeighbor(theGraph, e) == v) + { + if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) + return TRUE; + } + e = gp_GetNextArc(theGraph, e); + } + return FALSE; +} + +/******************************************************************** + gp_GetNeighborEdgeRecord() + Searches the adjacency list of u to obtains the edge record for v. + + NOTE: The caller should check whether the edge record is INONLY; + This method returns any edge record representing a connection + between vertices u and v, so this method can return an + edge record even if gp_IsNeighbor(theGraph, u, v) is false (0). + To filter out INONLY edge records, use gp_GetDirection() on + the edge record returned by this method. + + Returns NIL if there is no edge record indicating v in u's adjacency + list, or the edge record location otherwise. + ********************************************************************/ + +int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v) +{ + int e; + + if (gp_IsNotVertex(u) || gp_IsNotVertex(v)) + return NIL + NOTOK - NOTOK; + + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + if (gp_GetNeighbor(theGraph, e) == v) + return e; + + e = gp_GetNextArc(theGraph, e); + } + return NIL; +} + +/******************************************************************** + gp_GetVertexDegree() + + Counts the number of edge records in the adjacency list of a given + vertex V. + + Note: For digraphs, this method returns the total degree of the + vertex, including outward arcs (undirected and OUTONLY) + as well as INONLY arcs. Other functions are defined to get + the in-degree or out-degree of the vertex. + + Note: This function determines the degree by counting. An extension + could cache the degree value of each vertex and update the + cached value as edges are added and deleted. + ********************************************************************/ + +int gp_GetVertexDegree(graphP theGraph, int v) +{ + int e, degree; + + if (theGraph == NULL || gp_IsNotVertex(v)) + return 0 + NOTOK - NOTOK; + + degree = 0; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + degree++; + e = gp_GetNextArc(theGraph, e); + } + + return degree; +} + +/******************************************************************** + gp_GetVertexInDegree() + + Counts the number of edge records in the adjacency list of a given + vertex V that represent arcs from another vertex into V. + This includes undirected edges and INONLY arcs, so it only excludes + edges records that are marked as OUTONLY arcs. + + Note: This function determines the in-degree by counting. An extension + could cache the in-degree value of each vertex and update the + cached value as edges are added and deleted. + ********************************************************************/ + +int gp_GetVertexInDegree(graphP theGraph, int v) +{ + int e, degree; + + if (theGraph == NULL || gp_IsNotVertex(v)) + return 0 + NOTOK - NOTOK; + + degree = 0; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_OUTONLY) + degree++; + e = gp_GetNextArc(theGraph, e); + } + + return degree; +} + +/******************************************************************** + gp_GetVertexOutDegree() + + Counts the number of edge records in the adjacency list of a given + vertex V that represent arcs from V to another vertex. + This includes undirected edges and OUTONLY arcs, so it only excludes + edges records that are marked as INONLY arcs. + + Note: This function determines the out-degree by counting. An extension + could cache the out-degree value of each vertex and update the + cached value as edges are added and deleted. + ********************************************************************/ + +int gp_GetVertexOutDegree(graphP theGraph, int v) +{ + int e, degree; + + if (theGraph == NULL || gp_IsNotVertex(v)) + return 0 + NOTOK - NOTOK; + + degree = 0; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) + degree++; + e = gp_GetNextArc(theGraph, e); + } + + return degree; +} + +/******************************************************************** + gp_AttachArc() + + This routine adds newArc into v's adjacency list at a position + adjacent to the edge record for e, either before or after e, + depending on link. If e is not an arc (e.g. if e is NIL), + then link is assumed to indicate whether the new arc is to be + placed at the beginning or end of v's adjacency list. + + NOTE: The caller can pass NIL for v if e is not NIL, since the + vertex is implied (gp_GetNeighbor(theGraph, eTwin)) + + The arc is assumed to already exist in the data structure (i.e. + the storage of edges), as only a whole edge (two arcs) can be + inserted into or deleted from the data structure. Hence there is + no such thing as gp_InsertArc() or gp_DeleteArc(). + + See also gp_DetachArc(), gp_InsertEdge() and gp_DeleteEdge() + ********************************************************************/ + +void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc) +{ + if (gp_IsArc(e)) + { + int e2 = gp_GetAdjacentArc(theGraph, e, link); + + // e's link is newArc, and newArc's 1^link is e + gp_SetAdjacentArc(theGraph, e, link, newArc); + gp_SetAdjacentArc(theGraph, newArc, 1 ^ link, e); + + // newArcs's link is e2 + gp_SetAdjacentArc(theGraph, newArc, link, e2); + + // if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc + if (gp_IsArc(e2)) + gp_SetAdjacentArc(theGraph, e2, 1 ^ link, newArc); + else + gp_SetArc(theGraph, v, 1 ^ link, newArc); + } + else + { + int e2 = gp_GetArc(theGraph, v, link); + + // v's link is newArc, and newArc's 1^link is NIL + gp_SetArc(theGraph, v, link, newArc); + gp_SetAdjacentArc(theGraph, newArc, 1 ^ link, NIL); + + // newArcs's elink is e2 + gp_SetAdjacentArc(theGraph, newArc, link, e2); + + // if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc + if (gp_IsArc(e2)) + gp_SetAdjacentArc(theGraph, e2, 1 ^ link, newArc); + else + gp_SetArc(theGraph, v, 1 ^ link, newArc); + } +} + +/**************************************************************************** + gp_DetachArc() + + This routine detaches arc from its adjacency list, but it does not delete + it from the data structure (only a whole edge can be deleted). + + Some algorithms must temporarily detach an edge, perform some calculation, + and eventually put the edge back. This routine supports that operation. + The neighboring adjacency list nodes are cross-linked, but the two link + members of the arc are retained, so the arc can be reattached later by + invoking _RestoreArc(). A sequence of detached arcs can only be restored + in the exact opposite order of their detachment. Thus, algorithms do not + directly use this method to implement the temporary detach/restore method. + Instead, gp_HideEdge() and gp_RestoreEdge are used, and algorithms push + edge hidden edge onto the stack. One example of this stack usage is + provided by detaching edges with gp_ContractEdge() or gp_IdentifyVertices(), + and reattaching with gp_RestoreIdentifications(), which unwinds the stack + by invoking gp_RestoreVertex(). + ****************************************************************************/ + +void gp_DetachArc(graphP theGraph, int arc) +{ + int nextArc = gp_GetNextArc(theGraph, arc), + prevArc = gp_GetPrevArc(theGraph, arc); + + if (gp_IsArc(nextArc)) + gp_SetPrevArc(theGraph, nextArc, prevArc); + else + gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), prevArc); + + if (gp_IsArc(prevArc)) + gp_SetNextArc(theGraph, prevArc, nextArc); + else + gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), nextArc); +} + +/******************************************************************** + gp_AddEdge() + Adds the undirected edge (u,v) to the graph by placing edge records + representing u into v's circular edge record list and v into u's + circular edge record list. + + upos receives the location in G where the u record in v's list will be + placed, and vpos is the location in G of the v record we placed in + u's list. These are used to initialize the short circuit links. + + ulink (0|1) indicates whether the edge record to v in u's list should + become adjacent to u by its 0 or 1 link, i.e. u[ulink] == vpos. + vlink (0|1) indicates whether the edge record to u in v's list should + become adjacent to v by its 0 or 1 link, i.e. v[vlink] == upos. + + ********************************************************************/ + +int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink) +{ + int upos, vpos; + + if (theGraph == NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || + !gp_VirtualVertexInRange(theGraph, u) || !gp_VirtualVertexInRange(theGraph, v)) + return NOTOK; + + /* We enforce the edge limit */ + + if (theGraph->M >= theGraph->arcCapacity / 2) + return NONEMBEDDABLE; + + if (sp_NonEmpty(theGraph->edgeHoles)) + { + sp_Pop(theGraph->edgeHoles, vpos); + } + else + vpos = gp_EdgeInUseIndexBound(theGraph); + + upos = gp_GetTwinArc(theGraph, vpos); + + gp_SetNeighbor(theGraph, upos, v); + gp_AttachArc(theGraph, u, NIL, ulink, upos); + gp_SetNeighbor(theGraph, vpos, u); + gp_AttachArc(theGraph, v, NIL, vlink, vpos); + + theGraph->M++; + return OK; +} + +/******************************************************************** + gp_DynamicAddEdge() + Refer to documentation for gp_AddEdge for parameter description. + + Tries to call gp_AddEdge; if NONEMBEDDABLE, doubles the arc + capacity (up to a max of (N * (N-1))) using gp_EnsureArcCapacity, + then retries gp_AddEdge. + ********************************************************************/ +int gp_DynamicAddEdge(graphP theGraph, int u, int ulink, int v, int vlink) +{ + int Result = OK; + + if (theGraph == NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || + !gp_VirtualVertexInRange(theGraph, u) || !gp_VirtualVertexInRange(theGraph, v)) + return NOTOK; + + Result = gp_AddEdge(theGraph, u, ulink, v, vlink); + + if (Result == NONEMBEDDABLE) + { + int candidateArcCapacity = gp_GetArcCapacity(theGraph) * 2; + int N = theGraph->N; + int newArcCapacity = (candidateArcCapacity > (N * (N - 1))) ? (N * (N - 1)) : candidateArcCapacity; + Result = gp_EnsureArcCapacity(theGraph, newArcCapacity); + + if (Result != OK) + return Result; + + Result = gp_AddEdge(theGraph, u, ulink, v, vlink); + } + + return Result; +} + +/******************************************************************** + gp_InsertEdge() + + This function adds the edge (u, v) such that the edge record added + to the adjacency list of u is adjacent to e_u and the edge record + added to the adjacency list of v is adjacent to e_v. + The direction of adjacency is given by e_ulink for e_u and e_vlink + for e_v. Specifically, the new edge will be comprised of two arcs, + n_u and n_v. In u's (v's) adjacency list, n_u (n_v) will be added + so that it is indicated by e_u's (e_v's) e_ulink (e_vlink). + If e_u (or e_v) is not an arc, then e_ulink (e_vlink) indicates + whether to prepend or append to the adjacency list for u (v). + ********************************************************************/ + +int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink, + int v, int e_v, int e_vlink) +{ + int vertMax = gp_GetLastVirtualVertex(theGraph), + edgeMax = gp_EdgeInUseIndexBound(theGraph) - 1, + upos, vpos; + + if (theGraph == NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || + u > vertMax || v > vertMax || + e_u > edgeMax || (e_u < gp_GetFirstEdge(theGraph) && gp_IsArc(e_u)) || + e_v > edgeMax || (e_v < gp_GetFirstEdge(theGraph) && gp_IsArc(e_v)) || + e_ulink < 0 || e_ulink > 1 || e_vlink < 0 || e_vlink > 1) + return NOTOK; + + if (theGraph->M >= theGraph->arcCapacity / 2) + return NONEMBEDDABLE; + + if (sp_NonEmpty(theGraph->edgeHoles)) + { + sp_Pop(theGraph->edgeHoles, vpos); + } + else + vpos = gp_EdgeInUseIndexBound(theGraph); + + upos = gp_GetTwinArc(theGraph, vpos); + + gp_SetNeighbor(theGraph, upos, v); + gp_AttachArc(theGraph, u, e_u, e_ulink, upos); + + gp_SetNeighbor(theGraph, vpos, u); + gp_AttachArc(theGraph, v, e_v, e_vlink, vpos); + + theGraph->M++; + + return OK; +} + +/**************************************************************************** + gp_DeleteEdge() + + This function deletes the given edge record e and its twin, reducing the + number of edges M in the graph. + Before the e^th record is deleted, its 'nextLink' adjacency list neighbor + is collected as the return result. This is useful when iterating through + an edge list and making deletions because the nextLink arc is the 'next' + arc in the iteration, but it is hard to obtain *after* deleting e. + ****************************************************************************/ + +int gp_DeleteEdge(graphP theGraph, int e, int nextLink) +{ + // Calculate the nextArc after e so that, when e is deleted, the return result + // informs a calling loop of the next edge to be processed. + int nextArc = gp_GetAdjacentArc(theGraph, e, nextLink); + + // Delete the edge records e and eTwin from their adjacency lists. + gp_DetachArc(theGraph, e); + gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e)); + + // Clear the two edge records + // (the bit twiddle (e & ~1) chooses the lesser of e and its twin arc) +#ifdef USE_FASTER_1BASEDARRAYS + memset(theGraph->E + (e & ~1), NIL_CHAR, sizeof(edgeRec) << 1); +#else + _InitEdgeRec(theGraph, e); + _InitEdgeRec(theGraph, gp_GetTwinArc(theGraph, e)); +#endif + + // Now we reduce the number of edges in the data structure + theGraph->M--; + + // If records e and eTwin were not the last in the edge record array, + // then record a new hole in the edge array. */ + if (e < gp_EdgeInUseIndexBound(theGraph)) + { + sp_Push(theGraph->edgeHoles, e); + } + + // Return the previously calculated successor of e. + return nextArc; +} + +/******************************************************************** + _RestoreArc() + This routine reinserts an arc into the edge list from which it + was previously removed by gp_DetachArc(). + + The assumed processing model is that arcs will be restored in reverse + of the order in which they were hidden, i.e. it is assumed that the + hidden arcs will be pushed on a stack and the arcs will be popped + from the stack for restoration. + ********************************************************************/ + +void _RestoreArc(graphP theGraph, int arc) +{ + int nextArc = gp_GetNextArc(theGraph, arc), + prevArc = gp_GetPrevArc(theGraph, arc); + + if (gp_IsArc(nextArc)) + gp_SetPrevArc(theGraph, nextArc, arc); + else + gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc); + + if (gp_IsArc(prevArc)) + gp_SetNextArc(theGraph, prevArc, arc); + else + gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc); +} + +/******************************************************************** + gp_HideEdge() + This routine removes the two arcs of an edge from the adjacency lists + of its endpoint vertices, but does not delete them from the storage + data structure. + + Many algorithms must temporarily remove an edge, perform some + calculation, and eventually put the edge back. This routine supports + that operation. + + For each arc, the neighboring adjacency list nodes are cross-linked, + but the links in the arc are retained because they indicate the + neighbor arcs to which the arc can be reattached by gp_RestoreEdge(). + ********************************************************************/ + +void gp_HideEdge(graphP theGraph, int e) +{ + theGraph->functions.fpHideEdge(theGraph, e); +} + +void _HideEdge(graphP theGraph, int e) +{ + gp_DetachArc(theGraph, e); + gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e)); +} + +/******************************************************************** + gp_RestoreEdge() + This routine reinserts two two arcs of an edge into the adjacency + lists of the edge's endpoints, the arcs having been previously + removed by gp_HideEdge(). + + The assumed processing model is that edges will be restored in + reverse of the order in which they were hidden, i.e. it is assumed + that the hidden edges will be pushed on a stack and the edges will + be popped from the stack for restoration. + + Note: Since both arcs of an edge are restored, only one arc need + be pushed on the stack for restoration. This routine + restores the two arcs in the opposite order from the order + in which they are hidden by gp_HideEdge(). + ********************************************************************/ + +void gp_RestoreEdge(graphP theGraph, int e) +{ + theGraph->functions.fpRestoreEdge(theGraph, e); +} + +void _RestoreEdge(graphP theGraph, int e) +{ + _RestoreArc(theGraph, gp_GetTwinArc(theGraph, e)); + _RestoreArc(theGraph, e); +} + +/******************************************************************** + _HideInternalEdges() + Pushes onto the graph's stack and hides all arc nodes of the vertex + except the first and last arcs in the adjacency list of the vertex. + This method is typically called on a vertex that is on the external + face of a biconnected component, because the first and last arcs are + the ones that attach the vertex to the external face cycle, and any + other arcs in the adjacency list are inside that cycle. + + This method uses the stack. The caller is expected to clear the stack + or save the stack size before invocation, since the stack size is + needed to _RestoreInternalEdges(). + ********************************************************************/ + +int _HideInternalEdges(graphP theGraph, int vertex) +{ + int e = gp_GetFirstArc(theGraph, vertex); + + // If the vertex adjacency list is empty or if it contains + // only one edge, then there are no *internal* edges to hide + if (e == gp_GetLastArc(theGraph, vertex)) + return OK; + + // Start with the first internal edge + e = gp_GetNextArc(theGraph, e); + + // Cycle through all the edges, pushing each except stop + // before pushing the last edge, which is not internal + while (e != gp_GetLastArc(theGraph, vertex)) + { + sp_Push(theGraph->theStack, e); + gp_HideEdge(theGraph, e); + e = gp_GetNextArc(theGraph, e); + } + + return OK; +} + +/******************************************************************** + _RestoreInternalEdges() + Reverses the effects of _HideInternalEdges() + ********************************************************************/ + +int _RestoreInternalEdges(graphP theGraph, int stackBottom) +{ + return _RestoreHiddenEdges(theGraph, stackBottom); +} + +/******************************************************************** + _RestoreHiddenEdges() + + Each entry on the stack, down to stackBottom, is assumed to be an + edge record (arc) pushed in concert with invoking gp_HideEdge(). + Each edge is restored using gp_RestoreEdge() in exact reverse of the + hiding order. The stack is reduced in size to stackBottom. + + Returns OK on success, NOTOK on internal failure. + ********************************************************************/ + +int _RestoreHiddenEdges(graphP theGraph, int stackBottom) +{ + int e; + + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, e); + if (gp_IsNotArc(e)) + return NOTOK; + gp_RestoreEdge(theGraph, e); + } + + return OK; +} + +/******************************************************************** + gp_HideVertex() + + Pushes onto the graph's stack and hides all arc nodes of the vertex. + Additional integers are then pushed so that the result is reversible + by gp_RestoreVertex(). See that method for details on the expected + stack segment. + + Returns OK for success, NOTOK for internal failure. + ********************************************************************/ + +int gp_HideVertex(graphP theGraph, int vertex) +{ + if (gp_IsNotVertex(vertex)) + return NOTOK; + + return theGraph->functions.fpHideVertex(theGraph, vertex); +} + +int _HideVertex(graphP theGraph, int vertex) +{ + int hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack); + int e = gp_GetFirstArc(theGraph, vertex); + + // Cycle through all the edges, pushing and hiding each + while (gp_IsArc(e)) + { + sp_Push(theGraph->theStack, e); + gp_HideEdge(theGraph, e); + e = gp_GetNextArc(theGraph, e); + } + + // Push the additional integers needed by gp_RestoreVertex() + sp_Push(theGraph->theStack, hiddenEdgeStackBottom); + sp_Push(theGraph->theStack, NIL); + sp_Push(theGraph->theStack, NIL); + sp_Push(theGraph->theStack, NIL); + sp_Push(theGraph->theStack, NIL); + sp_Push(theGraph->theStack, NIL); + sp_Push(theGraph->theStack, vertex); + + return OK; +} + +/******************************************************************** + gp_ContractEdge() + + Contracts the edge e=(u,v). This hides the edge (both e and its + twin arc), and it also identifies vertex v with u. + See gp_IdentifyVertices() for further details. + + Returns OK for success, NOTOK for internal failure. + ********************************************************************/ + +int gp_ContractEdge(graphP theGraph, int e) +{ + if (gp_IsNotArc(e)) + return NOTOK; + + return theGraph->functions.fpContractEdge(theGraph, e); +} + +int _ContractEdge(graphP theGraph, int e) +{ + int eBefore, u, v; + + if (gp_IsNotArc(e)) + return NOTOK; + + u = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, e)); + v = gp_GetNeighbor(theGraph, e); + + eBefore = gp_GetNextArc(theGraph, e); + sp_Push(theGraph->theStack, e); + gp_HideEdge(theGraph, e); + + return gp_IdentifyVertices(theGraph, u, v, eBefore); +} + +/******************************************************************** + gp_IdentifyVertices() + + Identifies vertex v with vertex u by transferring all adjacencies + of v to u. Any duplicate edges are removed as described below. + The non-duplicate edges of v are added to the adjacency list of u + without disturbing their relative order, and they are added before + the edge record eBefore in u's list. If eBefore is NIL, then the + edges are simply appended to u's list. + + If u and v are adjacent, then gp_HideEdge() is invoked to remove + the edge e=(u,v). Then, the edges of v that indicate neighbors of + u are also hidden. This is done by setting the visited flags of + u's neighbors, then traversing the adjacency list of v. For each + visited neighbor of v, the edge is hidden because it would duplicate + an adjacency already expressed in u's list. Finally, the remaining + edges of v are moved to u's list, and each twin arc is adjusted + to indicate u as a neighbor rather than v. + + This routine assumes that the visited flags are clear beforehand, + and visited flag settings made herein are cleared before returning. + + The following are pushed, in order, onto the graph's built-in stack: + 1) an integer for each hidden edge + 2) the stack size before any hidden edges were pushed + 3) six integers that indicate u, v and the edges moved from v to u + + An algorithm that identifies a series of vertices, either through + directly calling this method or via gp_ContractEdge(), can unwind + the identifications using gp_RestoreIdentifications(), which + invokes gp_RestoreVertex() repeatedly. + + Returns OK on success, NOTOK on internal failure + ********************************************************************/ + +int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore) +{ + return theGraph->functions.fpIdentifyVertices(theGraph, u, v, eBefore); +} + +int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore) +{ + int e = gp_GetNeighborEdgeRecord(theGraph, u, v); + int hiddenEdgeStackBottom, eBeforePred; + + // If the vertices are adjacent, then the identification is + // essentially an edge contraction with a bit of fixup. + if (gp_IsArc(e)) + { + int result = gp_ContractEdge(theGraph, e); + + // The edge contraction operation pushes one hidden edge then + // recursively calls this method. This method then pushes K + // hidden edges then an integer indicating where the top of + // stack was before the edges were hidden. That integer + // indicator must be decremented, thereby incrementing the + // number of hidden edges to K+1. + // After pushing the K hidden edges and the stackBottom of + // the hidden edges, the recursive call to this method pushes + // six more integers to indicate edges that were moved from + // v to u, so the "hidden edges stackBottom" is in the next + // position down. + int hiddenEdgesStackBottomIndex = sp_GetCurrentSize(theGraph->theStack) - 7; + int hiddenEdgesStackBottomValue = sp_Get(theGraph->theStack, hiddenEdgesStackBottomIndex); + + sp_Set(theGraph->theStack, hiddenEdgesStackBottomIndex, hiddenEdgesStackBottomValue - 1); + + return result; + } + + // Now, u and v are not adjacent. Before we do any edge hiding or + // moving, we record the current stack size, as this is the + // stackBottom for the edges that will be hidden next. + hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack); + + // Mark as visited all neighbors of u + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) + return NOTOK; + + gp_SetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); + e = gp_GetNextArc(theGraph, e); + } + + // For each edge record of v, if the neighbor is visited, then + // push and hide the edge. + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) + { + sp_Push(theGraph->theStack, e); + gp_HideEdge(theGraph, e); + } + e = gp_GetNextArc(theGraph, e); + } + + // Mark as unvisited all neighbors of u + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); + e = gp_GetNextArc(theGraph, e); + } + + // Push the hiddenEdgeStackBottom as a record of how many hidden + // edges were pushed (also, see above for Contract Edge adjustment) + sp_Push(theGraph->theStack, hiddenEdgeStackBottom); + + // Moving v's adjacency list to u is aided by knowing the predecessor + // of u's eBefore (the edge record in u's list before which the + // edge records of v will be added). + eBeforePred = gp_IsArc(eBefore) + ? gp_GetPrevArc(theGraph, eBefore) + : gp_GetLastArc(theGraph, u); + + // Turns out we only need to record six integers related to the edges + // being moved in order to easily restore them later. + sp_Push(theGraph->theStack, eBefore); + sp_Push(theGraph->theStack, gp_GetLastArc(theGraph, v)); + sp_Push(theGraph->theStack, gp_GetFirstArc(theGraph, v)); + sp_Push(theGraph->theStack, eBeforePred); + sp_Push(theGraph->theStack, u); + sp_Push(theGraph->theStack, v); + + // For the remaining edge records of v, reassign the 'v' member + // of each twin arc to indicate u rather than v. + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), u); + e = gp_GetNextArc(theGraph, e); + } + + // If v has any edges left after hiding edges, indicating common neighbors with u, ... + if (gp_IsArc(gp_GetFirstArc(theGraph, v))) + { + // Then perform the list union of v into u between eBeforePred and eBefore + if (gp_IsArc(eBeforePred)) + { + if (gp_IsArc(gp_GetFirstArc(theGraph, v))) + { + gp_SetNextArc(theGraph, eBeforePred, gp_GetFirstArc(theGraph, v)); + gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), eBeforePred); + } + } + else + { + gp_SetFirstArc(theGraph, u, gp_GetFirstArc(theGraph, v)); + } + + if (gp_IsArc(eBefore)) + { + if (gp_IsArc(gp_GetLastArc(theGraph, v))) + { + gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), eBefore); + gp_SetPrevArc(theGraph, eBefore, gp_GetLastArc(theGraph, v)); + } + } + else + { + gp_SetLastArc(theGraph, u, gp_GetLastArc(theGraph, v)); + } + + gp_SetFirstArc(theGraph, v, NIL); + gp_SetLastArc(theGraph, v, NIL); + } + + return OK; +} + +/******************************************************************** + gp_RestoreVertex() + + This method assumes the built-in graph stack contents are the result + of vertex hide, vertex identify and edge contract operations. + This content consists of segments of integers, each segment + corresponding to the removal of a vertex during an edge contraction + or vertex identification in which a vertex v was merged into a + vertex u. The segment contains two blocks of integers. + The first block contains information about u, v, the edge records + in v's adjacency list that were added to u, and where in u's + adjacency list they were added. The second block of integers + contains a list of edges incident to v that were hidden from the + graph because they were incident to neighbors of v that were also + neighbors of u (so they would have produced duplicate edges had + they been left in v's adjacency list when it was merged with u's + adjacency list). + + This method pops the first block of the segment off the stack and + uses the information to help remove v's adjacency list from u and + restore it into v. Then, the second block is removed from the + stack, and each indicated edge is restored from the hidden state. + + It is anticipated that this method will be overloaded by extension + algorithms to perform some processing as each vertex is restored. + Before restoration, the topmost segment has the following structure: + + ... FHE ... LHE HESB e_u_succ e_v_last e_v_first e_u_pred u v + ^------------| + + FHE = First hidden edge + LHE = Last hidden edge + HESB = Hidden edge stack bottom + e_u_succ, e_u_pred = The edges of u between which the edges of v + were inserted. NIL can appear if the edges of v + were added to the beginning or end of u's list + e_v_first, e_v_last = The first and last edges of v's list, once + the hidden edges were removed + + Returns OK for success, NOTOK for internal failure. + ********************************************************************/ + +int gp_RestoreVertex(graphP theGraph) +{ + return theGraph->functions.fpRestoreVertex(theGraph); +} + +int _RestoreVertex(graphP theGraph) +{ + int u, v, e_u_succ, e_u_pred, e_v_first, e_v_last, HESB, e; + + if (sp_GetCurrentSize(theGraph->theStack) < 7) + return NOTOK; + + sp_Pop(theGraph->theStack, v); + sp_Pop(theGraph->theStack, u); + sp_Pop(theGraph->theStack, e_u_pred); + sp_Pop(theGraph->theStack, e_v_first); + sp_Pop(theGraph->theStack, e_v_last); + sp_Pop(theGraph->theStack, e_u_succ); + + // If u is not NIL, then vertex v was identified with u. Otherwise, v was + // simply hidden, so we skip to restoring the hidden edges. + if (gp_IsVertex(u)) + { + // Remove v's adjacency list from u, including accounting for degree 0 case + if (gp_IsArc(e_u_pred)) + { + gp_SetNextArc(theGraph, e_u_pred, e_u_succ); + // If the successor edge exists, link it to the predecessor, + // otherwise the predecessor is the new last arc + if (gp_IsArc(e_u_succ)) + gp_SetPrevArc(theGraph, e_u_succ, e_u_pred); + else + gp_SetLastArc(theGraph, u, e_u_pred); + } + else if (gp_IsArc(e_u_succ)) + { + // The successor arc exists, but not the predecessor, + // so the successor is the new first arc + gp_SetPrevArc(theGraph, e_u_succ, NIL); + gp_SetFirstArc(theGraph, u, e_u_succ); + } + else + { + // Just in case u was degree zero + gp_SetFirstArc(theGraph, u, NIL); + gp_SetLastArc(theGraph, u, NIL); + } + + // Place v's adjacency list into v, including accounting for degree 0 case + gp_SetFirstArc(theGraph, v, e_v_first); + gp_SetLastArc(theGraph, v, e_v_last); + if (gp_IsArc(e_v_first)) + gp_SetPrevArc(theGraph, e_v_first, NIL); + if (gp_IsArc(e_v_last)) + gp_SetPrevArc(theGraph, e_v_last, NIL); + + // For each edge record restored to v's adjacency list, reassign the 'v' member + // of each twin arc to indicate v rather than u. + e = e_v_first; + while (gp_IsArc(e)) + { + gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), v); + e = (e == e_v_last ? NIL : gp_GetNextArc(theGraph, e)); + } + } + + // Restore the hidden edges of v, if any + sp_Pop(theGraph->theStack, HESB); + return _RestoreHiddenEdges(theGraph, HESB); +} + +/******************************************************************** + gp_RestoreVertices() + + This method assumes the built-in graph stack has content consistent + with numerous vertex identification or edge contraction operations. + This method unwinds the stack, moving edges back to their original + vertex owners and restoring hidden edges. + This method is a simple iterator that invokes gp_RestoreVertex() + until the stack is empty, so extension algorithms are more likely + to overload gp_RestoreVertex(). + + Returns OK for success, NOTOK for internal failure. + ********************************************************************/ + +int gp_RestoreVertices(graphP theGraph) +{ + while (sp_NonEmpty(theGraph->theStack)) + { + if (gp_RestoreVertex(theGraph) != OK) + return NOTOK; + } + + return OK; +} + +/**************************************************************************** + _ComputeArcType() + This is just a little helper function that automates a sequence of decisions + that has to be made a number of times. + An edge record is being added to the adjacency list of a; it indicates that + b is a neighbor. The edgeType can be either 'tree' (EDGE_TYPE_PARENT or + EDGE_TYPE_CHILD) or 'cycle' (EDGE_TYPE_BACK or EDGE_TYPE_FORWARD). + If a or b is a root copy, we translate to the non-virtual counterpart, + then wedetermine which has the lesser DFI. If a has the lower DFI then the + edge record is a tree edge to a child (EDGE_TYPE_CHILD) if edgeType indicates + a tree edge. If edgeType indicates a cycle edge, then it is a forward cycle + edge (EDGE_TYPE_FORWARD) to a descendant. + Symmetric conditions define the types for a > b. + ****************************************************************************/ + +int _ComputeArcType(graphP theGraph, int a, int b, int edgeType) +{ + a = gp_IsVirtualVertex(theGraph, a) ? gp_GetPrimaryVertexFromRoot(theGraph, a) : a; + b = gp_IsVirtualVertex(theGraph, b) ? gp_GetPrimaryVertexFromRoot(theGraph, b) : b; + + if (a < b) + return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_CHILD : EDGE_TYPE_FORWARD; + + return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_PARENT : EDGE_TYPE_BACK; +} + +/**************************************************************************** + _SetEdgeType() + When we are restoring an edge, we must restore its type (tree edge or cycle edge). + We can deduce what the type was based on other information in the graph. Each + arc of the edge gets the appropriate type setting (parent/child or back/forward). + This method runs in constant time plus the degree of vertex u, or constant + time if u is known to have a degree bound by a constant. + ****************************************************************************/ + +int _SetEdgeType(graphP theGraph, int u, int v) +{ + int e, eTwin, u_orig, v_orig; + + // If u or v is a virtual vertex (a root copy), then get the non-virtual counterpart. + u_orig = gp_IsVirtualVertex(theGraph, u) ? (gp_GetPrimaryVertexFromRoot(theGraph, u)) : u; + v_orig = gp_IsVirtualVertex(theGraph, v) ? (gp_GetPrimaryVertexFromRoot(theGraph, v)) : v; + + // Get the edge for which we will set the type + + e = gp_GetNeighborEdgeRecord(theGraph, u, v); + eTwin = gp_GetTwinArc(theGraph, e); + + // If u_orig is the parent of v_orig, or vice versa, then the edge is a tree edge + + if (gp_GetVertexParent(theGraph, v_orig) == u_orig || + gp_GetVertexParent(theGraph, u_orig) == v_orig) + { + if (u_orig > v_orig) + { + gp_ResetEdgeType(theGraph, e, EDGE_TYPE_PARENT); + gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_CHILD); + } + else + { + gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_PARENT); + gp_ResetEdgeType(theGraph, e, EDGE_TYPE_CHILD); + } + } + + // Otherwise it is a back edge + + else + { + if (u_orig > v_orig) + { + gp_ResetEdgeType(theGraph, e, EDGE_TYPE_BACK); + gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD); + } + else + { + gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_BACK); + gp_ResetEdgeType(theGraph, e, EDGE_TYPE_FORWARD); + } + } + + return OK; +} + +/******************************************************************** + _DeleteUnmarkedEdgesInBicomp() + + This function deletes from a given biconnected component all edges + whose visited member is zero. + + The stack is used but preserved. In debug mode, NOTOK can result if + there is a stack overflow. This method pushes at most one integer + per vertex in the bicomp. + + Returns OK on success, NOTOK on implementation failure + ********************************************************************/ + +int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot) +{ + int V, e; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + + e = gp_GetFirstArc(theGraph, V); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetEdgeVisited(theGraph, e) ? gp_GetNextArc(theGraph, e) : gp_DeleteEdge(theGraph, e, 0); + } + } + return OK; +} + +/******************************************************************** + _ClearInvertedFlagsInBicomp() + + This function clears the inverted flag markers on any edges in a + given biconnected component. + + The stack is used but preserved. In debug mode, NOTOK can result if + there is a stack overflow. This method pushes at most one integer + per vertex in the bicomp. + + Returns OK on success, NOTOK on implementation failure + ********************************************************************/ + +int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot) +{ + int V, e; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + + e = gp_GetFirstArc(theGraph, V); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + { + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + gp_ClearEdgeFlagInverted(theGraph, e); + } + + e = gp_GetNextArc(theGraph, e); + } + } + return OK; +} + +/******************************************************************** + _GetBicompSize() + + Determine the number of vertices in the bicomp. + + The stack is used but preserved. In debug mode, NOTOK can result if + there is a stack overflow. This method pushes at most one integer + per vertex in the bicomp. + + Returns a positive number on success, NOTOK on implementation failure + ********************************************************************/ + +int _GetBicompSize(graphP theGraph, int BicompRoot) +{ + int V, e; + int theSize = 0; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + theSize++; + e = gp_GetFirstArc(theGraph, V); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetNextArc(theGraph, e); + } + } + return theSize; +} + +/******************************************************************** + debugNOTOK() + This function provides a non-void wrapper for exit(). + This is useful for debugging as it allows compilation of an exit + command in places where NOTOK is returned. + In exhaustive testing, we want to bail on the first NOTOK that occurs. + Comment out the exit() call to get a stack trace. + ********************************************************************/ + +int debugNOTOK(void) +{ + // exit(-1); + return 0; // NOTOK is normally defined to be zero +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.c new file mode 100644 index 000000000..05dcd2a40 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graph.h" + +/* Imported functions */ + +extern void _ClearVisitedFlags(graphP); + +extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); +extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); +extern int _JoinBicomps(graphP theGraph); + +extern int _MarkHighestXYPath(graphP theGraph); + +extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); +extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); +extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); + +extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); + +extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); + +extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); + +extern int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); +extern int _IsolateOuterplanarityObstructionA(graphP theGraph); +extern int _IsolateOuterplanarityObstructionB(graphP theGraph); + +/* Private function declarations for K_{2,3} searching */ + +int _SearchForK23InBicomp(graphP theGraph, int v, int R); +int _IsolateOuterplanarityObstructionE1orE2(graphP theGraph); +int _IsolateOuterplanarityObstructionE3orE4(graphP theGraph); + +/**************************************************************************** + _SearchForK23InBicomp() + ****************************************************************************/ + +int _SearchForK23InBicomp(graphP theGraph, int v, int R) +{ + isolatorContextP IC = &theGraph->IC; + int X, Y, XPrevLink, YPrevLink; + + /* Begin by determining whether minor A, B or E is detected */ + + if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) + return NOTOK; + + /* Minors A and B result in the desired K_{2,3} homeomorph, + so we isolate it and return NONEMBEDDABLE. */ + + if (theGraph->IC.minorType & (MINORTYPE_A | MINORTYPE_B)) + { + _ClearVisitedFlags(theGraph); + + if (theGraph->IC.minorType & MINORTYPE_A) + { + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + + if (_IsolateOuterplanarityObstructionA(theGraph) != OK) + return NOTOK; + } + else if (theGraph->IC.minorType & MINORTYPE_B) + { + int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); + + if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE) + return NOTOK; + + if (_IsolateOuterplanarityObstructionB(theGraph) != OK) + return NOTOK; + } + + if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* For minor E (a K_4) , we run the additional tests to see if a K_{2,3} is + entangled with the K_4. If not, then we return OK to indicate that + the outerplanarity embedder should proceed as if the K_4 had not + been found. */ + + /* If any vertices other than R, X, Y and W exist along the + external face, then we can obtain a K_{2,3} by minor E1 or E2 */ + + X = IC->x; + Y = IC->y; + XPrevLink = 1; + YPrevLink = 0; + if (IC->w != _GetNeighborOnExtFace(theGraph, X, &XPrevLink) || + IC->w != _GetNeighborOnExtFace(theGraph, Y, &YPrevLink)) + { + _ClearVisitedFlags(theGraph); + + if (_IsolateOuterplanarityObstructionE1orE2(theGraph) != OK) + return NOTOK; + + if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* If X, Y or W make either a direct back edge connection or a + connection through a separated child bicomp to an ancestor of + the current vertex v, then we can obtain a K_{2,3} by minor + E3 or E4. Note that this question is query on X, Y and W is + equivalent to the planarity version of external activity. */ + + gp_UpdateVertexFuturePertinentChild(theGraph, X, v); + gp_UpdateVertexFuturePertinentChild(theGraph, Y, v); + gp_UpdateVertexFuturePertinentChild(theGraph, IC->w, v); + if (FUTUREPERTINENT(theGraph, X, v) || + FUTUREPERTINENT(theGraph, Y, v) || + FUTUREPERTINENT(theGraph, IC->w, v)) + { + _ClearVisitedFlags(theGraph); + + if (_IsolateOuterplanarityObstructionE3orE4(theGraph) != OK) + return NOTOK; + + if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* The extra cases for finding a K_{2,3} failed, so the bicomp rooted + by R is a separable subgraph of the input that is isomorphic + to K_4. So, we restore the original vertex orientation of + the bicomp (because it's polite, not because we really have to). + Then, we return OK to tell the outerplanarity embedder that it + can ignore this K_4 and keep processing. */ + + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateOuterplanarityObstructionE1orE2() + ****************************************************************************/ + +int _IsolateOuterplanarityObstructionE1orE2(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + int XPrevLink = 1; + + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + + /* Isolate E1 */ + + if (theGraph->IC.px != theGraph->IC.x) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->w) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK) + return NOTOK; + } + else if (theGraph->IC.py != theGraph->IC.y) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->r) != OK) + return NOTOK; + } + + /* Isolate E2 */ + + else if (IC->w != _GetNeighborOnExtFace(theGraph, IC->x, &XPrevLink)) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->y) != OK) + return NOTOK; + } + + else + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->r) != OK) + return NOTOK; + } + + /* Final bits are in common */ + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE || + theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateOuterplanarityObstructionE3orE4() + ****************************************************************************/ + +int _IsolateOuterplanarityObstructionE3orE4(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + int u, d, XorY; + + // Minor E3 + gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.x, theGraph->IC.v); + gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.y, theGraph->IC.v); + if (FUTUREPERTINENT(theGraph, theGraph->IC.x, theGraph->IC.v) || + FUTUREPERTINENT(theGraph, theGraph->IC.y, theGraph->IC.v)) + { + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + + gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.x, theGraph->IC.v); + if (FUTUREPERTINENT(theGraph, theGraph->IC.x, theGraph->IC.v)) + XorY = theGraph->IC.x; + else + XorY = theGraph->IC.y; + + /* The cases of X future pertinent and Y future pertinent + are the same except for the bicomp external face marking + (because parameter order is important) */ + + if (XorY == theGraph->IC.x) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK) + return NOTOK; + } + else + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) + return NOTOK; + } + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + + if (_FindUnembeddedEdgeToAncestor(theGraph, XorY, &u, &d) != TRUE) + return NOTOK; + + if (theGraph->functions.fpMarkDFSPath(theGraph, u, IC->v) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, XorY, d) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkEdge(theGraph, u, d) != OK || + _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) + return NOTOK; + + return OK; + } + + /* Otherwise, isolate Minor E4 (reduce to minor A) */ + + if (_FindUnembeddedEdgeToAncestor(theGraph, IC->w, &u, &d) != TRUE) + return NOTOK; + + IC->v = u; + IC->dw = d; + return _IsolateOuterplanarityObstructionA(theGraph); +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.h new file mode 100644 index 000000000..d7941a93f --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_K23SEARCH_H +#define GRAPH_K23SEARCH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graphStructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define K23SEARCH_NAME "K23Search" + + int gp_AttachK23Search(graphP theGraph); + int gp_DetachK23Search(graphP theGraph); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.private.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.private.h new file mode 100644 index 000000000..3aee68cb8 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search.private.h @@ -0,0 +1,28 @@ +#ifndef GRAPH_K23SEARCH_PRIVATE_H +#define GRAPH_K23SEARCH_PRIVATE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graph.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + // Overloaded function pointers + graphFunctionTable functions; + + } K23SearchContext; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c new file mode 100644 index 000000000..86b1ed932 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK23Search_Extensions.c @@ -0,0 +1,248 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "graphK23Search.private.h" +#include "graphK23Search.h" + +extern int _SearchForK23InBicomp(graphP theGraph, int v, int R); + +extern int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts); +extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, + int *imageVerts, int maxNumImageVerts); +extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); + +/* Forward declarations of overloading functions */ + +int _K23Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); +int _K23Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); +int _K23Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); +int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); + +/* Forward declarations of functions used by the extension system */ + +void *_K23Search_DupContext(void *pContext, void *theGraph); +void _K23Search_FreeContext(void *); + +/**************************************************************************** + * K23SEARCH_ID - the variable used to hold the integer identifier for this + * extension, enabling this feature's extension context to be distinguished + * from other features' extension contexts that may be attached to a graph. + ****************************************************************************/ + +int K23SEARCH_ID = 0; + +/**************************************************************************** + gp_AttachK23Search() + + This function adjusts the graph data structure to attach the K2,3 search + feature. + ****************************************************************************/ + +int gp_AttachK23Search(graphP theGraph) +{ + K23SearchContext *context = NULL; + + // If the K2,3 search feature has already been attached to the graph + // then there is no need to attach it again + gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); + if (context != NULL) + { + return OK; + } + + // Allocate a new extension context + context = (K23SearchContext *)malloc(sizeof(K23SearchContext)); + if (context == NULL) + { + return NOTOK; + } + + // Put the overload functions into the context function table. + // gp_AddExtension will overload the graph's functions with these, and + // return the base function pointers in the context function table + memset(&context->functions, 0, sizeof(graphFunctionTable)); + + context->functions.fpHandleBlockedBicomp = _K23Search_HandleBlockedBicomp; + context->functions.fpEmbedPostprocess = _K23Search_EmbedPostprocess; + context->functions.fpCheckEmbeddingIntegrity = _K23Search_CheckEmbeddingIntegrity; + context->functions.fpCheckObstructionIntegrity = _K23Search_CheckObstructionIntegrity; + + // Store the K23 search context, including the data structure and the + // function pointers, as an extension of the graph + if (gp_AddExtension(theGraph, &K23SEARCH_ID, (void *)context, + _K23Search_DupContext, _K23Search_FreeContext, + &context->functions) != OK) + { + _K23Search_FreeContext(context); + return NOTOK; + } + + return OK; +} + +/******************************************************************** + gp_DetachK23Search() + ********************************************************************/ + +int gp_DetachK23Search(graphP theGraph) +{ + return gp_RemoveExtension(theGraph, K23SEARCH_ID); +} + +/******************************************************************** + _K23Search_DupContext() + ********************************************************************/ + +void *_K23Search_DupContext(void *pContext, void *theGraph) +{ + K23SearchContext *context = (K23SearchContext *)pContext; + K23SearchContext *newContext = (K23SearchContext *)malloc(sizeof(K23SearchContext)); + + if (newContext != NULL) + { + *newContext = *context; + } + + return newContext; +} + +/******************************************************************** + _K23Search_FreeContext() + ********************************************************************/ + +void _K23Search_FreeContext(void *pContext) +{ + free(pContext); +} + +/******************************************************************** + ********************************************************************/ + +int _K23Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) +{ + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) + { + // If R is the root of a descendant bicomp of v, we push it, but then we know the search for K2,3 + // will be successful and return NONEMBEDDABLE because this condition corresponds to minor A, which + // is a K2,3. Thus, an "OK to proceed with Walkdown searching elsewhere" result cannot happen, + // so we don't have to test for it to detect if we have to pop these two back off the stack. + if (R != RootVertex) + sp_Push2(theGraph->theStack, R, 0); + + // The possible results here are NONEMBEDDABLE if a K2,3 homeomorph is found, or OK if only + // a K4 was found and unblocked such that it is OK for the Walkdown to continue searching + // elsewhere. Note that the OK result can only happen if RootVertex==R since minor E can only + // happen on a child bicomp of vertex v, not a descendant bicomp. + return _SearchForK23InBicomp(theGraph, v, R); + } + + else + { + K23SearchContext *context = NULL; + gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K23Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + // For K2,3 search, we just return the edge embedding result because the + // search result has been obtained already. + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) + { + return edgeEmbeddingResult; + } + + // When not searching for K2,3, we let the superclass do the work + else + { + K23SearchContext *context = NULL; + gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K23Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) +{ + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) + { + return OK; + } + + // When not searching for K2,3, we let the superclass do the work + else + { + K23SearchContext *context = NULL; + gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) +{ + // When searching for K2,3, we ensure that theGraph is a subgraph of + // the original graph and that it contains a K2,3 homeomorph + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) + { + int degrees[4], imageVerts[5]; + + if (_TestSubgraph(theGraph, origGraph) != TRUE) + return NOTOK; + + if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK) + return NOTOK; + + if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE) + { + return OK; + } + + return NOTOK; + } + + // When not searching for K2,3, we let the superclass do the work + else + { + K23SearchContext *context = NULL; + gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); + } + } + + return NOTOK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c new file mode 100644 index 000000000..71ad4cb68 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c @@ -0,0 +1,2007 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "graphK33Search.h" +#include "graphK33Search.private.h" + +// extern int K33SEARCH_ID; + +#include "../graph.h" + +/* Imported functions */ + +// extern void _ClearVisitedFlags(graphP); +extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); +extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); +extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); +extern int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue); + +// extern int _GetBicompSize(graphP theGraph, int BicompRoot); +extern int _HideInternalEdges(graphP theGraph, int vertex); +extern int _RestoreInternalEdges(graphP theGraph, int stackBottom); +extern int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot); +extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); +extern int _SetEdgeType(graphP theGraph, int u, int v); + +extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); +extern int _JoinBicomps(graphP theGraph); +extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); +extern int _OrientVerticesInEmbedding(graphP theGraph); +// extern void _InvertVertex(graphP theGraph, int V); +extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); + +extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); +extern int _MarkLowestXYPath(graphP theGraph); +extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); + +extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); +extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); +extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); + +extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); + +extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); + +extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); + +extern int _IsolateMinorE1(graphP theGraph); +// extern int _IsolateMinorE2(graphP theGraph); +extern int _IsolateMinorE3(graphP theGraph); +extern int _IsolateMinorE4(graphP theGraph); + +extern int _MarkDFSPathsToDescendants(graphP theGraph); +extern int _AddAndMarkUnembeddedEdges(graphP theGraph); + +extern void _K33Search_InitEdgeRec(K33SearchContext *context, int e); + +/* Private functions for K_{3,3} searching. */ + +int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R); + +int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context); +int _SearchForMinorE1(graphP theGraph); +int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context); +int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max); +int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex); +int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int vertex, + int closerAncestor, int fartherAncestor); +int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, + int cutVertex, int *pDescendant); +int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker); +int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker); + +int _MarkLowestXYPath(graphP theGraph); +int _TestForZtoWPath(graphP theGraph); +int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max); +int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot); +int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink); +int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R); +int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType); +int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType); +int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e); +int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context); + +int _IsolateMinorE5(graphP theGraph); +int _IsolateMinorE6(graphP theGraph, K33SearchContext *context); +int _IsolateMinorE7(graphP theGraph, K33SearchContext *context); + +/**************************************************************************** + _SearchForK33InBicomp() + ****************************************************************************/ + +int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R) +{ + isolatorContextP IC = &theGraph->IC; + int tempResult; + + /* Begin by determining which non-planarity minor is detected */ + + if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK) + return NOTOK; + + /* If minor A is selected, then the root of the oriented bicomp has been changed */ + + else + R = IC->r; + + /* Minors A to D result in the desired K_{3,3} homeomorph, + so we isolate it and return NONEMBEDDABLE. */ + + if (theGraph->IC.minorType & (MINORTYPE_A | MINORTYPE_B | MINORTYPE_C | MINORTYPE_D)) + { + /* First we restore the orientations of the vertices in the + one bicomp we have messed with so that there is no confusion. */ + + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) + return NOTOK; + + /* Next we restore the orientation of the embedding so we + can restore the reduced paths (because we avoid modifying + the Kuratowski subgraph isolator to restore reduced paths, + which are a construct of the K_{3,3} search). */ + + if (_OrientVerticesInEmbedding(theGraph) != OK || + _RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + /* Next we simply call the Kuratowski subgraph isolation since + we know now that it will isolate a K_{3,3}. + For minor A, we need to set up the stack that would be + available immediately after a Walkdown failure. */ + + if (theGraph->IC.minorType & MINORTYPE_A) + { + sp_ClearStack(theGraph->theStack); + sp_Push2(theGraph->theStack, R, NIL); + } + + if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* For minor E (a K5 minor), we run the additional tests to see if minors E1 to E4 apply + since these minors isolate a K_{3,3} entangled with the K5. + This is the key location where GetLeastAncestorConnection() must be constant time. */ + + IC->ux = _Fast_GetLeastAncestorConnection(theGraph, context, IC->x); + IC->uy = _Fast_GetLeastAncestorConnection(theGraph, context, IC->y); + IC->uz = _Fast_GetLeastAncestorConnection(theGraph, context, IC->z); + + if (IC->z != IC->w || + IC->uz > MAX(IC->ux, IC->uy) || + (IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) || + (IC->x != IC->px || IC->y != IC->py)) + { + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) + return NOTOK; + + if (_OrientVerticesInEmbedding(theGraph) != OK || + _RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* If the Kuratowski subgraph isolator will not isolate a K_{3,3} based on minor E, + then a K5 homeomorph could be isolated. However, a K_{3,3} may still be tangled + with the K5, so we now run the additional tests of the K_{3,3} search algorithm. + + If the search finds a K_{3,3} (tempResult of NONEMBEDDABLE), then we remove unwanted + edges from the graph and return NONEMBEDDABLE. If the search has a fault (NOTOK), + then we return. If the result is OK, then a K_{3,3} was not found at this time + and we proceed with some clean-up work below. */ + + if ((tempResult = _RunExtraK33Tests(theGraph, context)) != OK) + { + if (tempResult == NONEMBEDDABLE) + if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + return tempResult; + } + + /* The extra cases for finding a K_{3,3} did not succeed, so the bicomp rooted by R + is either a K5 homeomorph (with at most a superficially entangled K_{3,3}) or + we have made the special setting that allows us to detect the one "merge blocker" + case that would be too costly to try now. Either way, we can safely reduce the + bicomp to the 4-cycle (R, X, W, Y, R) and proceed with the planarity algorithm. + We also restore the mixed orientation of the bicomp (i.e. the proper + orientation in the context of the edge signs) because this code can work + when ReduceBicomp doesn't do any actual work. */ + + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) + return NOTOK; + + if (_ReduceBicomp(theGraph, context, R) != OK) + return NOTOK; + + /* Set visitedInfo values in the bicomp to the initialized state so the planarity + algorithm can properly do the Walkup procedure in future steps */ + + if (_FillVertexVisitedInfoInBicomp(theGraph, IC->r, theGraph->N) != OK) + return NOTOK; + + /* We now intend to ignore the pertinence of W (conceptually eliminating + the connection from W to the current vertex). Note that none of the + bicomp roots in the pertinentRootsList (nor their respective subtrees) + will be visited again by the planarity algorithm because they must've + been only pertinent. If they were future pertinent and pertinent, + then we would've found a K_{3,3} by non-planarity minor B. Thus, the original + Walkup costs that identified the pertinent bicomps we intend to ignore are + one-time costs, preserving linear time. */ + + gp_SetVertexPertinentEdge(theGraph, IC->w, NIL); + gp_SetVertexPertinentRootsList(theGraph, IC->w, NIL); + + return OK; +} + +/**************************************************************************** + _RunExtraK33Tests() + ****************************************************************************/ + +#define USE_MERGEBLOCKER + +int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context) +{ + isolatorContextP IC = &theGraph->IC; + int u_max = MAX3(IC->ux, IC->uy, IC->uz); + +#ifndef USE_MERGEBLOCKER + int u; +#endif + + /* Case 1: If there is a pertinent or future pertinent vertex other than W + on the lower external face path between X and Y (the points of + attachment of the x-y path), then we can isolate a K_{3,3} homeomorph + by Minor E1. */ + + if (_SearchForMinorE1(theGraph) != OK) + return NOTOK; + + if (IC->w != IC->z) + { + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE1(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* Case 2: If W/Z can make an external connection to an ancestor of V + that is descendant to u_{max}, then a K_{3,3} homeomorph can + be isolated with Minor E2. + + OPTIMIZATION: We do not need to check for this case. + We avoid doing so because in very specially crafted cases + it could be too costly if the connection doesn't exist. + However, if the highest numbered ancestor H of the current vertex + that has an external connection from W is a descendant u_{max} + then we will discover a K_{3,3} by Minor A or B in step H + (unless some other test succeeds at finding a K_{3,3} first), + so we just let the non-planarity detector do its work since + Minors A and B both provide a K_{3,3} when found. + + This happens because w is pertinent to H and future pertinent + to u_max or an ancestor of u_max. + + Minor A will happen if, in step H, Walkdown descends to the + bicomp containing the current vertex, x, y and w. Since x + and y would still be future pertinent (they connect to u_max + or higher, i.e. with lesser DFI, than u_max). + + Minor B will happen if the bicomp containing the current vertex, + x, y and w is a descendant of a bicomp that blocks planarity + in step H. The bicomp would be both pertinent (due to w's + connection to H) and future pertinent(due to connections to + ancestors of H by w, x and y). + */ + +#ifndef USE_MERGEBLOCKER + u = _SearchForDescendantExternalConnection(theGraph, context, IC->w, u_max); + if (u > u_max) + { + IC->uz = u; + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE2(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } +#endif + + /* Case 3: If X or Y can make an external connection to an ancestor of V + that is descendant to u_{max}, then a K_{3,3} homeomorph + can be isolated with Minor E3. + + NOTE: Due to the prior use of the Kuratowski subgraph + isolator, we know that at most one of X, Y or W/Z could have an + external connection to an ancestor of u_{max} = MAX(ux, uy, uz). + + OPTIMIZATION: We do not check for the lower connection required + to find Minor E3 because it might ultimately be too costly. + Instead, we mark the vertex with a 'merge blocker' of u_{max}. + If the planar embedder attempts to merge the vertex prior to step + u_{max}, then the embedder has found the desired connection and a + K_{3,3} homeomorph is isolated at that time. + */ + +#ifdef USE_MERGEBLOCKER + context->VI[IC->x].mergeBlocker = u_max; +#endif +#ifndef USE_MERGEBLOCKER + u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max); + if (u > u_max) + { + IC->ux = u; + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE3(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } +#endif + +#ifdef USE_MERGEBLOCKER + context->VI[IC->y].mergeBlocker = u_max; +#endif +#ifndef USE_MERGEBLOCKER + u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max); + if (u > u_max) + { + IC->uy = u; + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE3(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } +#endif + + /* Case 4: If there exists any x-y path with points of attachment px and py + such that px!=x or py!=y, then a K_{3,3} homeomorph can be isolated + with Minor E4. */ + + // Prior tests to choose the type of non-planarity minor selected the highest + // x-y path, so we need to clear the visited flags of that path before marking + // instead the x-y path with the lowest attachment points (those closest to W + // along the external face). + if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) + return NOTOK; + + // Now mark the lowest x-y path so that we can test whether _any_ x-y path + // has points of attachment, px or py, below x or y, respectively (where + // below means closer to W than x or y, respectively, along the external face). + if (_MarkLowestXYPath(theGraph) != TRUE) + return NOTOK; + + // Now we test for E4 based on whether px!=x or py!=y. Note that the inequality + // test is sufficient because not equal means attached lower by the time we are + // testing for E4 because the prior test for non-planarity minor C already ruled + // out the possibility that inequality could mean attached higher than x or y. + if (IC->px != IC->x || IC->py != IC->y) + { + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE4(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* Case 5: If the x-y path contains an internal vertex that starts a second + internal path from the internal vertex to W/Z, then a K_{3,3} homeomorph + can be isolated with Minor E5. */ + + // Since the E4 test above has already marked the lowest X-Y path, and only + // the lowest one could possibly have a Z-to-W path attached to it, we + // simply reuse the x-y path from E4 here in the E5 test. + // (NOTE: Only the lowest X-Y path could have a Z-to-W path because all + // bicomps are planar embeddings, and so a Z-to-W path emanating + // from a higher X-Y path would cross the lowest one, violating the + // planarity of the bicomp). + if (_TestForZtoWPath(theGraph) != OK) + return NOTOK; + + if (gp_GetVertexVisited(theGraph, IC->w)) + { + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE5(theGraph) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + /* Case 6: If uz < u_{max} and there is an external connection (other than external + connections involving X, Y and W/Z) between an ancestor of u_{max} and a + vertex in the range [V...u_{max}), then a K_{3,3} homeomorph can be + isolated with Minor E6. + + OPTIMIZATION: See _TestForStraddlingBridge() */ + + if (IC->uz < u_max) + { + if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max))) + { + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE6(theGraph, context) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + } + + /* Case 7: If ux < u_{max} or uy < u_{max} and there is an external connection + between an ancestor of u_{max} and a vertex in the range [V...u_{max}) + (except for external connections involving X, Y and W/Z), then a K_{3,3} + homeomorph can be isolated with Minor E7. + + OPTIMIZATION: Same as Case 6.*/ + + if (IC->ux < u_max || IC->uy < u_max) + { + if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max))) + { + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE7(theGraph, context) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + } + + /* If none of the tests found a K_{3,3}, then we return OK to indicate that nothing + went wrong, but a K_{3,3} was not found. */ + + return OK; +} + +/**************************************************************************** + _SearchForMinorE1() + Search along the external face below the x-y path for a vertex Z other + than W that is future pertinent or pertinent. + ****************************************************************************/ + +int _SearchForMinorE1(graphP theGraph) +{ + int Z = theGraph->IC.px, ZPrevLink = 1; + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + + while (Z != theGraph->IC.py) + { + if (Z != theGraph->IC.w) + { + gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); + if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) + { + theGraph->IC.z = Z; + theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); + return OK; + } + else if (PERTINENT(theGraph, Z)) + { + /* Swap the roles of W and Z */ + + theGraph->IC.z = theGraph->IC.w; + theGraph->IC.w = Z; + + /* If the new W (indicated by Z) was on the path (R, X, old W) then + the new Z (the old W, which has no type mark) is on the path + (X, new W, new Z, Y) so we change the type new Z to being on the + RYW path. Otherwise, the order is (X, new Z, new W, Y), so the + new Z (old W with no type) is type changed to be on the RXW path.*/ + + if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) + gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RYW); + else + gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RXW); + + /* For completeness, we change the new W to type unknown */ + + gp_ClearVertexObstructionType(theGraph, theGraph->IC.w); + + /* The external activity ancestor connection of the new Z must be obtained */ + + theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, theGraph->IC.z); + + return OK; + } + } + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + return OK; +} + +/**************************************************************************** + _FinishIsolatorContextInitialization() + Once it has been decided that a desired subgraph can be isolated, it + becomes safe to finish the isolator context initialization. + ****************************************************************************/ + +int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context) +{ + isolatorContextP IC = &theGraph->IC; + + /* Restore the orientation of the bicomp on which we're working, then + perform orientation of all vertices in graph. (An unnecessary but + polite step that simplifies the description of key states of the + data structures). */ + + if (_OrientVerticesInBicomp(theGraph, IC->r, 1) != OK) + return NOTOK; + + if (_OrientVerticesInEmbedding(theGraph) != OK) + return NOTOK; + + /* Restore any paths that were reduced to single edges */ + + if (_RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + /* We assume that the current bicomp has been marked appropriately, + but we must now clear the visitation flags of all other bicomps. */ + + if (_ClearVisitedFlagsInOtherBicomps(theGraph, IC->r) != OK) + return NOTOK; + + /* To complete the normal behavior of _ClearVisitedFlags() in the + normal isolator context initialization, we also have to clear + the visited flags on all edges that have not yet been embedded */ + + _ClearVisitedFlagsInUnembeddedEdges(theGraph); + + /* Now we can find the descendant ends of unembedded back edges based on + the ancestor settings ux, uy and uz. */ + + if (_FindExternalConnectionDescendantEndpoint(theGraph, IC->ux, IC->x, &IC->dx) != OK || + _FindExternalConnectionDescendantEndpoint(theGraph, IC->uy, IC->y, &IC->dy) != OK || + _FindExternalConnectionDescendantEndpoint(theGraph, IC->uz, IC->z, &IC->dz) != OK) + return NOTOK; + + /* Finally, we obtain the descendant end of an unembedded back edge to + the current vertex. */ + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _Fast_GetLeastAncestorConnection() + + This function searches for an ancestor of the current vertex v adjacent by a + cycle edge to the given cutVertex or one of its DFS descendants appearing in + a separated bicomp. The given cutVertex is assumed to be future pertinent + such that either the leastAncestor or the lowpoint of a separated DFS child + is less than v. We obtain the minimum possible connection from the cutVertex + to an ancestor of v. + + This function performs the same operation as _GetLeastAncestorConnection(), + except in constant time. + ****************************************************************************/ + +int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex) +{ + int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); + int child = context->VI[cutVertex].separatedDFSChildList; + + if (gp_IsVertex(child) && ancestor > gp_GetVertexLowpoint(theGraph, child)) + ancestor = gp_GetVertexLowpoint(theGraph, child); + + return ancestor; +} + +/**************************************************************************** + _GetAdjacentAncestorInRange() + Returns the ancestor of theVertex that is adjacent to theVertex by an + unembedded back edge and has a DFI strictly between closerAncestor and + fartherAncestor. + Returns NIL if theVertex has no such neighboring ancestor. + ****************************************************************************/ + +int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int theVertex, + int closerAncestor, int fartherAncestor) +{ + int e = context->VI[theVertex].backArcList; + + while (gp_IsArc(e)) + { + if (gp_GetNeighbor(theGraph, e) < closerAncestor && + gp_GetNeighbor(theGraph, e) > fartherAncestor) + return gp_GetNeighbor(theGraph, e); + + e = gp_GetNextArc(theGraph, e); + if (e == context->VI[theVertex].backArcList) + e = NIL; + } + return NIL; +} + +/**************************************************************************** + _SearchForDescendantExternalConnection() + Search the cutVertex and each separated child subtree for an external + connection to a vertex ancestor to the current vertex V and descendant to u_max. + + The function returns the descendant of u_max found to have an external + connection to the given cut vertex. + ****************************************************************************/ + +int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max) +{ + isolatorContextP IC = &theGraph->IC; + int u2 = _GetAdjacentAncestorInRange(theGraph, context, cutVertex, IC->v, u_max); + int child, descendant; + + // Test cutVertex for an external connection to descendant of u_max via direct back edge + if (gp_IsVertex(u2)) + return u2; + + // If there is no direct back edge connection from the cut vertex + // to a vertex on the path between V and u_max, then we will + // look for such a connection in the DFS subtrees rooted by + // separated DFS children of the vertex (ignoring those whose + // lowpoint indicates that they make no external connections) + + // Begin by pushing the separated DFS children of the cut vertex with + // lowpoints indicating connections to ancestors of the current vertex. + sp_ClearStack(theGraph->theStack); + child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex); + while (gp_IsVertex(child)) + { + if (gp_GetVertexLowpoint(theGraph, child) < IC->v && gp_IsSeparatedDFSChild(theGraph, child)) + sp_Push(theGraph->theStack, child); + child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); + } + + // Now process the stack until it is empty or until we've found the desired connection. + while (!sp_IsEmpty(theGraph->theStack)) + { + sp_Pop(theGraph->theStack, descendant); + + // If the vertex has a lowpoint indicating that it makes no external connections, + // then skip the subtree rooted by the vertex + if (gp_GetVertexLowpoint(theGraph, descendant) < IC->v) + { + // Check the subtree root for the desired connection. + u2 = _GetAdjacentAncestorInRange(theGraph, context, descendant, IC->v, u_max); + if (gp_IsVertex(u2)) + return u2; + + // Push each child as a new subtree root to be considered, except skip those whose lowpoint is too great. + child = gp_GetVertexSortedDFSChildList(theGraph, descendant); + while (gp_IsVertex(child)) + { + if (gp_GetVertexLowpoint(theGraph, child) < IC->v) + sp_Push(theGraph->theStack, child); + + child = gp_GetVertexNextDFSChild(theGraph, descendant, child); + } + } + } + + // The only external connections from the cutVertex lead to u_max, so return it. + return u_max; +} + +/**************************************************************************** + _FindExternalConnectionDescendantEndpoint() + + This operation is similar to _FindUnembeddedEdgeToAncestor() except that + we need to be more precise in this case, finding an external connection + from a given cut vertex to a *particular* given ancestor. + + NOTE: By external we don't mean externall active so much as not embedded in + the bicomp containing the cut vertex. + + Returns OK if it finds that either the given cutVertex or one of its + descendants in a separated bicomp has an unembedded back edge + connection to the given ancestor vertex. + Returns NOTOK otherwise (it is an error to not find the descendant because + this function is only called if _SearchForDescendantExternalConnection() + has already determined the existence of the descendant). + ****************************************************************************/ + +int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, + int cutVertex, int *pDescendant) +{ + int child, e; + + // Check whether the cutVertex is directly adjacent to the ancestor + // by an unembedded back edge. + + e = gp_GetVertexFwdArcList(theGraph, ancestor); + while (gp_IsArc(e)) + { + if (gp_GetNeighbor(theGraph, e) == cutVertex) + { + *pDescendant = cutVertex; + return OK; + } + + e = gp_GetNextArc(theGraph, e); + if (e == gp_GetVertexFwdArcList(theGraph, ancestor)) + e = NIL; + } + + // Now check the descendants of the cut vertex to see if any make + // a connection to the ancestor. + child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex); + while (gp_IsVertex(child)) + { + if (gp_GetVertexLowpoint(theGraph, child) < theGraph->IC.v && gp_IsSeparatedDFSChild(theGraph, child)) + { + if (_FindUnembeddedEdgeToSubtree(theGraph, ancestor, child, pDescendant) == TRUE) + return OK; + } + child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); + } + + return NOTOK; +} + +/**************************************************************************** + _SearchForMergeBlocker() + + This function helps to implement the merge blocking optimization of + _SearchForDescendantExternalConnection(). The function RunExtraK33Tests() + sets a mergeBlocker rather than run _SearchForDescendantExternalConnection() + in certain cases. This procedure is called by MergeBicomps to test the + embedding stack for a merge blocker before merging any biconnected components. + If a merge blocker is found, then FindK33WithMergeBlocker() is called and + ultimately the embedder's Walkdown function is terminated since a K_{3,3} + is isolated. + + Returns OK on success (whether or not the search found a merge blocker) + NOTOK on internal function failure + pMergeBlocker is set to NIL unless a merge blocker is found. + ****************************************************************************/ + +int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker) +{ + stackP tempStack; + int Z; + + /* Set return result to 'not found' then return if there is no stack to inspect */ + + *pMergeBlocker = NIL; + + if (sp_IsEmpty(theGraph->theStack)) + return OK; + + /* Create a copy of the embedding stack */ + + tempStack = sp_Duplicate(theGraph->theStack); + if (tempStack == NULL) + return NOTOK; + + /* Search the copy of the embedding stack for a merge blocked vertex */ + + while (!sp_IsEmpty(tempStack)) + { + sp_Pop2_Discard(tempStack); /* Move (R, Rout) out of the way */ + sp_Pop2_Discard1(tempStack, Z); /* Get Z, discard ZPrevLink */ + + if (gp_IsVertex(context->VI[Z].mergeBlocker) && + context->VI[Z].mergeBlocker < v) + { + *pMergeBlocker = Z; + break; + } + } + + sp_Free(&tempStack); + return OK; +} + +/**************************************************************************** + _FindK33WithMergeBlocker() + + This function completes the merge blocking optimization by isolating a K_{3,3} + based on minor E3 if a merge blocked vertex was previously found. + + Returns OK on success, NOTOK on internal function failure + ****************************************************************************/ + +int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker) +{ + int R, RPrevLink, u_max, u, e; + isolatorContextP IC = &theGraph->IC; + + /* First, we orient the vertices so we can successfully restore all of the + reduced paths. This needs to be done before reconstructing the context + for CASE 3 of RunExtraK33Tests() because the reconstruction involves + using the Walkup to v from a descendant of v, which will not work if + the descendant is in one of the reduced paths. */ + + if (_OrientVerticesInEmbedding(theGraph) != OK || + _RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + /* Reconstruct the context that was present for CASE 3 of RunExtraK33Tests() + when we decided to set a mergeBlocker rather than calling + _SearchForDescendantExternalConnection() */ + + /* Obtain the root of the bicomp containing the mergeBlocker. */ + + RPrevLink = 1; + R = mergeBlocker; + while (gp_IsNotVirtualVertex(theGraph, R)) + R = _GetNeighborOnExtFace(theGraph, R, &RPrevLink); + + /* Switch the 'current step' variable v to be equal to the + non-virtual counterpart of the bicomp root. */ + + IC->v = gp_GetPrimaryVertexFromRoot(theGraph, R); + + /* Reinitialize the visitation, pertinence and future pertinence settings from step u_max for step v */ + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + gp_SetVertexVisitedInfo(theGraph, v, theGraph->N); + gp_SetVertexPertinentEdge(theGraph, v, NIL); + gp_SetVertexPertinentRootsList(theGraph, v, NIL); + + // Any calls to actually determine FUTUREPERTINENT status for a vertex w will actually invoke + // gp_UpdateVertexFuturePertinentChild(theGraph, w, v) beforehand, so only need to reinitialize here + gp_SetVertexFuturePertinentChild(theGraph, v, gp_GetVertexSortedDFSChildList(theGraph, v)); + } + + /* Restore the pertinence settings of step v by doing the Walkup for each + back edge that was not embedded when step v was originally performed. */ + + e = gp_GetVertexFwdArcList(theGraph, IC->v); + while (gp_IsArc(e)) + { + theGraph->functions.fpWalkUp(theGraph, IC->v, e); + + e = gp_GetNextArc(theGraph, e); + if (e == gp_GetVertexFwdArcList(theGraph, IC->v)) + e = NIL; + } + + /* Next, we make the standard initialization calls for when we have found + a non-planarity condition. */ + + sp_ClearStack(theGraph->theStack); + + if (_ChooseTypeOfNonplanarityMinor(theGraph, IC->v, R) != OK) + return NOTOK; + + IC->ux = _GetLeastAncestorConnection(theGraph, IC->x); + IC->uy = _GetLeastAncestorConnection(theGraph, IC->y); + IC->uz = _GetLeastAncestorConnection(theGraph, IC->z); + + u_max = MAX3(IC->ux, IC->uy, IC->uz); + + /* Perform the remainder of CASE 3 of RunExtraK33Tests() */ + + if (mergeBlocker == IC->x) + { + u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max); + if (u > u_max) + { + IC->ux = u; + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE3(theGraph) != OK) + return NOTOK; + } + else + return NOTOK; + } + else if (mergeBlocker == IC->y) + { + u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max); + if (u > u_max) + { + IC->uy = u; + if (_FinishIsolatorContextInitialization(theGraph, context) != OK || + _IsolateMinorE3(theGraph) != OK) + return NOTOK; + } + else + return NOTOK; + } + else + return NOTOK; + + /* Do the final clean-up to obtain the K_{3,3} */ + + if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _TestForZtoWPath() + + This function tests whether there is a path inside the bicomp leading from W + to some internal node of the x-y path. If there is, the path is marked (the + visited flags of its vertices and edges are set). + + Upon function return, the marking (visited flag setting) of W distinguishes + whether the path was found. + + The function returns NOTOK on internal error, OK otherwise. + + Preconditions: The X-Y path is marked visited by a prior invocation of + the meothd _MarkLowestXYPath() above. + + So, we start a depth first search from W to find a visited vertex, except + we prune the search to ignore vertices whose obstruction type is other than + unknown. This ensures the path found, if any, avoids external face vertices, + including avoiding X and Y. Furthermore, the path search is completed without + traversing to R due to the obstructing X-Y path. + + The depth first search has to "mark" the vertices it has seen as visited, + but the visited flags are already in use to distinguish the X-Y path. + So, we reuse the visitedInfo setting of each vertex. The core planarity + algorithm makes settings between 0 and N, so we will regard all of those + as indicating 'unvisited' by this method, and use -1 to indicate visited. + These markings need not be cleaned up because, if the desired path is found + the a K_{3,3} is isolated and if the desired path is not found then the + bicomp is reduced and the visitedInfo in the remaining vertices are set + appropriately for future Walkup processing of the core planarity algorithm. + + For each vertex we visit, if it is an internal vertex on the X-Y path + (i.e. visited flag set and obstruction type unknown), then we want to stop + and unroll the stack to obtain the desired path (described below). If the + vertex is internal but not on the X-Y path (i.e. visited flag clear and + obstruction type unknown), then we want to visit its neighbors, except + those already marked visited by this method (i.e. those with visitedInfo + of -1) and those with a known obstruction type. + + We want to manage the stack so that it when the desired vertex is found, + the stack contains the desired path. So, we do not simply push all the + neighbors of the vertex being visited. First, given that we have popped + some vertex-edge pair (v, e), we push *only* the next edge after e in + v's adjacency list (starting with the first if e is NIL) that leads to a + new 'eligible' vertex. An eligible vertex is one whose obstruction type + is unknown and whose visitedInfo is other than -1 (so, internal and not + yet processed by this method). Second, when we decide a new vertex w + adjacent to v is eligible, we push not only (v, e) but also (w, NIL). + When we later pop the vertex-edge pair containing NIL, we know that + the vertex obstruction type is unknown so we test whether its visited + flag is set (indicating an internal vertex on the X-Y path). If so, then + we can stop the depth first search, then use the vertices and edges + remaining on the stack to mark the desired path from the external face + vertex W to an internal vertex Z on the X-Y path. + + If we pop (v, NIL) and find that the visited flag of v is clear, then it + is not the desired connection endpoint to the X-Y path. We need to process + all paths extending from it, but we don't want any of those paths to cycle + back to this vertex, so we mark it as ineligible by putting -1 in its + visitedInfo member. This is also the case in which the _first_ edge record e + leading from v to an eligible vertex w is obtained, whereupon we push both + (v, e) and (w, NIL). Eventually all paths leading from w to eligible + vertices will be explored, and if none find the desired vertex connection + to the X-Y path, then (v, e) is popped. Now we search the adjacency list of + v starting after e to find the _next_ edge record that indicates the an + eligible vertex to visit. None of the vertices processed while visiting paths + extending from w will be eligible anymore, so it can be seen that this method + is a depth first search. If no remaining edges from v indicate eligible + vertices, then nothing is pushed and we simply go to the next iteration, + which pops a 2-tuple containing the vertex u and the edge record e that + points to v. Finally, if the stack empties without finding the desired vertex, + then the first loop ends, and the second main loop does not mark a path because + the stack is empty. + ****************************************************************************/ + +int _TestForZtoWPath(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + int v, e, w; + + sp_ClearStack(theGraph->theStack); + sp_Push2(theGraph->theStack, IC->w, NIL); + + while (!sp_IsEmpty(theGraph->theStack)) + { + sp_Pop2(theGraph->theStack, v, e); + + if (gp_IsNotArc(e)) + { + // If the vertex is visited, then it is a member of the X-Y path + // Because it is being popped, its obstruction type is unknown because + // that is the only kind of vertex pushed. + // Hence, we break because we've found the desired path. + if (gp_GetVertexVisited(theGraph, v)) + break; + + // Mark this vertex as being visited by this method (i.e. ineligible + // to have processing started on it again) + gp_SetVertexVisitedInfo(theGraph, v, -1); + + e = gp_GetFirstArc(theGraph, v); + } + else + e = gp_GetNextArc(theGraph, e); + + // This while loop breaks on the first edge it finds that is eligible to be + // pushed. Once that happens, we break. The successive edges of a vertex are + // only pushed (see the else clause above) once all paths extending from v + // through e have been explored and found not to contain the desired path + while (gp_IsArc(e)) + { + w = gp_GetNeighbor(theGraph, e); + + // The test for w being a virtual vertex is just safeguarding the two subsequent calls, + // but it can never happen due to the obstructing X-Y path. + if (gp_IsNotVirtualVertex(theGraph, w) && + gp_GetVertexVisitedInfo(theGraph, w) != -1 && + gp_GetVertexObstructionType(theGraph, w) == VERTEX_OBSTRUCTIONTYPE_UNKNOWN) + { + sp_Push2(theGraph->theStack, v, e); + sp_Push2(theGraph->theStack, w, NIL); + + break; + } + + e = gp_GetNextArc(theGraph, e); + } + } + + while (!sp_IsEmpty(theGraph->theStack)) + { + sp_Pop2(theGraph->theStack, v, e); + gp_SetVertexVisited(theGraph, v); + gp_SetEdgeVisited(theGraph, e); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + } + + return OK; +} + +/**************************************************************************** + _TestForStraddlingBridge() + We proceed on the path [V...u_{max}) from the current vertex V up to and + excluding u_{max}. For each vertex p, we test whether p has a least + ancestor less than u_{max} and whether p has a DFS child c that is not an + ancestor of X, Y and W and that has a connection to an ancestor of u_{max} + (in other words, whether the child C has a lowpoint less than u_{max}). + + The sortedDFSChildLIst of the vertex p is scanned for the separated DFS + child c of least lowpoint, excluding the ancestor of X, Y and W. + + If no bridge straddling u_{max} is found, the function returns NIL. + If a straddling bridge is found, the function returns a descendant d + of p in the subtree rooted by c such that d has a leastAncestor less + than u_{max}. Given the vertex d, the path through the straddling + bridge required in Minors E6 and E7 is easy to identify: Mark the + DFS tree path from d to p, and add and mark the edge from d to its + least ancestor. + + OPTIMIZATION: If a straddling bridge is not found, then in each tree edge of + the path [V...u_{max}) we set the member noStraddle equal to u_{max}. + Then, we modify the above stated routine so that if it is testing + for a straddling bridge of u_{max} along this path, it will stop + if it encounters an edge with noStraddle equal to u_{max}. + Also, the optimization will only set noStraddle equal to + u_{max} on the portion of the path that is traversed. Finally, if + noStraddle is set to a value other than NIL, the setting will be + ignored and it will not be changed. + + Due to this optimization, we do not traverse a path more than once + to find out whether a vertex on the path has a bridge that straddles + u_{max}. This leaves two questions: + 1) What if a future step must determine whether there is a + straddling bridge of an ancestor of u_{max}? + 2) What if a future step must determine whether there is a + straddling bridge of a descendant of u_{max}? + + The condition described in the first question cannot occur because it + would imply the ability to detect a straddling bridge now. + The condition described by the second question may occur, but in the + future step, the bicomp now being tested for a K_{3,3} will be part of + a straddling bridge in that future step. Thus, the straddling + bridge query is asked at most twice along any DFS tree path. + ****************************************************************************/ + +int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max) +{ + isolatorContextP IC = &theGraph->IC; + int p, c, d, excludedChild, e; + + p = IC->v; + excludedChild = gp_GetDFSChildFromRoot(theGraph, IC->r); + d = NIL; + + // Starting at V, traverse the ancestor path to u_max looking for a straddling bridge + while (p > u_max) + { + // If we find a direct edge from p to an ancestor of u_max, the break. + if (gp_GetVertexLeastAncestor(theGraph, p) < u_max) + { + d = p; + break; + } + + // Check for a path from p to an ancestor of u_max using the child of p + // with the least Lowpoint, except the child that is an ancestor of X, Y and W. + // It is possible to do this just using the sortedDFSChildList, but no point + // in not using the separatedDFSChildList + /* + { + int c = gp_GetVertexSortedDFSChildList(theGraph, p); + while (gp_IsVertex(c)) + { + if (c != excludedChild && gp_IsSeparatedDFSChild(theGraph, c)) + { + if (gp_GetVertexLowpoint(theGraph, c) < u_max) + break; + } + + c = gp_GetVertexNextDFSChild(theGraph, p, c); + } + } + */ + c = context->VI[p].separatedDFSChildList; + if (c == excludedChild) + c = LCGetNext(context->separatedDFSChildLists, c, c); + + if (gp_IsVertex(c) && gp_GetVertexLowpoint(theGraph, c) < u_max) + { + _FindUnembeddedEdgeToSubtree(theGraph, gp_GetVertexLowpoint(theGraph, c), c, &d); + break; + } + + // Check for noStraddle of u_max, break if found + e = gp_GetFirstArc(theGraph, p); + if (context->E[e].noStraddle == u_max) + break; + + // Go to the next ancestor + excludedChild = p; + p = gp_GetVertexParent(theGraph, p); + } + + // If d is NIL, then no straddling bridge was found, so we do the noStraddle optimization. + if (gp_IsNotVertex(d)) + { + c = IC->v; + while (c != p) + { + e = gp_GetFirstArc(theGraph, c); + if (gp_IsVertex(context->E[e].noStraddle)) + break; + + context->E[e].noStraddle = u_max; + + c = gp_GetVertexParent(theGraph, c); + } + } + + // Return either NIL indicating no bridge straddling u_max or the descendant d + // used to help mark a straddling bridge that was found by this test. + return d; +} + +/**************************************************************************** + _ReduceBicomp() + + We want to reduce the given biconnected component to a 4-cycle plus an + internal edge connecting X and Y. Each edge is to be associated with a + path from the original graph, preserving the depth first search tree + paths that help connect the vertices R, X, Y, and W. If a K_{3,3} is later found, + the paths are restored, but it is necessary to preserve the DFS tree so that + functions like MarkDFSPath() will be able to pass through the restored bicomp. + Also, if a K_{3,3} is later found due to the merge blocker optimization, then the + internal X-Y path may be needed and, once the bicomp reduction is reversed, + a full DFS subtree connecting all vertices in the bicomp will need to be + restored or else functions that traverse the bicomp will not work. + + For example, _FindK33WithMergeBlocker() invokes ChooseTypeOfNonplanarityMinor() + to help reconstruct the context under which the mergeBlocker was set. + ChooseTypeOfNonplanarityMinor() calls _ClearVisitedFlagsInBicomp(), which + depends on the DFS tree. + + NOTE: The following are some general steps taken in this method: + 1) All edges in the bicomp are marked unvisited + 2) selected paths are marked visited + 3) unvisited edges are deleted + 4) the edges of the bicomp are marked unvisited again + 5) the remaining paths of the bicomp are reduced + Some of the edges that get deleted in step 3 above may represent + paths that were reduced in prior embedder iterations. We delete + the reduction edge but not the path it represents. + If a K_{3,3} is ever found, then the edges of these reduced paths + are still in the graph, though not connected to anything important. + The desired K_{3,3} is marked visited, but step 4 above ensures that + these reduction paths are not marked visited. Hence, they will be + deleted when the K_{3,3} is isolated, and this routine does not + need to restore any reduced paths on the edges it deletes. + We also don't (and don't have the time to) restore any reduction + edges along the paths we intend to keep. + ****************************************************************************/ + +int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R) +{ + isolatorContextP IC = &theGraph->IC; + int min, max, A, A_edge, B, B_edge; + int rxType, xwType, wyType, yrType, xyType; + + /* The vertices in the bicomp need to be oriented so that functions + like MarkPathAlongBicompExtFace() will work. */ + + if (_OrientVerticesInBicomp(theGraph, R, 0) != OK) + return NOTOK; + + /* The reduced edges start with a default type of 'tree' edge. The + tests below, which identify the additional non-tree paths + needed to complete the reduced bicomp, also identify which + reduced edges need to be cycle edges.*/ + + rxType = xwType = wyType = yrType = xyType = EDGE_TYPE_PARENT; + + /* Now we calculate some values that help figure out the shape of the + DFS subtree whose structure will be retained in the bicomp. */ + + min = MIN3(IC->x, IC->y, IC->w); + max = MAX3(IC->x, IC->y, IC->w); + // int mid = MAX3(MIN(IC->x, IC->y), MIN(IC->x, IC->w), MIN(IC->y, IC->w)); + + /* If the order of descendendancy from V goes first to X, then it can + proceed either to W then Y or to Y then W */ + + if (min == IC->x) + { + /* A is a descendant adjacent to the current vertex by a cycle edge + whose DFS tree path to either mid or max is combined with the + cycle edge to form the path that will be reduced to the + external face cycle edge (V, max). */ + + A_edge = gp_GetLastArc(theGraph, IC->r); + A = gp_GetNeighbor(theGraph, A_edge); + yrType = EDGE_TYPE_BACK; + + /* If Y is max, then a path parallel to the X-Y path will be a + second path reduced to a cycle edge. We find the neighbor B + of min=X on the X-Y path. The edge (B, min) is a cycle edge + that, along with the DFS tree path (B, ..., max), will be + retained and reduced to a cycle edge. */ + + if (max == IC->y) + { + B_edge = gp_GetLastArc(theGraph, IC->x); + while (B_edge != gp_GetFirstArc(theGraph, IC->x)) + { + if (gp_GetEdgeVisited(theGraph, B_edge)) + break; + B_edge = gp_GetPrevArc(theGraph, B_edge); + } + + if (!gp_GetEdgeVisited(theGraph, B_edge)) + return NOTOK; + + B = gp_GetNeighbor(theGraph, B_edge); + xyType = EDGE_TYPE_BACK; + } + + /* Otherwise, W is max so we find the neighbor B of min=X on the + lower external face path (X, ..., W), which excludes V. The + cycle edge (B, min) and the DFS tree path (B, max) will be + retained and reduced to a cycle edge.*/ + + else if (max == IC->w) + { + B_edge = gp_GetFirstArc(theGraph, IC->x); + B = gp_GetNeighbor(theGraph, B_edge); + xwType = EDGE_TYPE_BACK; + } + + else + return NOTOK; + } + + /* Otherwise, the order of descendancy from V goes first to Y, then it + proceeds to either W then X or to X then W. The */ + + else + { + A_edge = gp_GetFirstArc(theGraph, IC->r); + A = gp_GetNeighbor(theGraph, A_edge); + rxType = EDGE_TYPE_BACK; + + if (max == IC->x) + { + B_edge = gp_GetFirstArc(theGraph, IC->y); + while (B_edge != gp_GetLastArc(theGraph, IC->y)) + { + if (gp_GetEdgeVisited(theGraph, B_edge)) + break; + B_edge = gp_GetNextArc(theGraph, B_edge); + } + + if (!gp_GetEdgeVisited(theGraph, B_edge)) + return NOTOK; + + B = gp_GetNeighbor(theGraph, B_edge); + xyType = EDGE_TYPE_BACK; + } + + else if (max == IC->w) + { + B_edge = gp_GetLastArc(theGraph, IC->y); + B = gp_GetNeighbor(theGraph, B_edge); + wyType = EDGE_TYPE_BACK; + } + + else + return NOTOK; + } + + /* Now that we have collected the information on which cycle edge and + which tree paths will actually be retained, we clear the visited + flags so the current X-Y path will not be retained (an X-Y path + formed mostly or entirely from DFS tree edges is retained). */ + + if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + + /* Now we mark the tree path from the maximum numbered vertex up + to the bicomp root. This marks one of the following four paths: + Case 1. (V, ..., X=min, ..., W=mid, ..., Y=max) + Case 2. (V, ..., X=min, ..., Y=mid, ..., W=max) + Case 3. (V, ..., Y=min, ..., W=mid, ..., X=max) + Case 4. (V, ..., Y=min, ..., X=mid, ..., W=max) */ + + if (theGraph->functions.fpMarkDFSPath(theGraph, R, max) != OK) + return NOTOK; + + /* Now we use A to mark a path on the external face corresponding to: + Case 1. (V, ..., Y=max) + Case 2. (V, ..., Y=mid) + Case 3. (V, ..., X=max) + Case 4. (V, ..., X=mid) */ + + if (theGraph->functions.fpMarkDFSPath(theGraph, min == IC->x ? IC->y : IC->x, A) != OK) + return NOTOK; + + gp_SetEdgeVisited(theGraph, A_edge); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, A_edge)); + + /* Now we use B to mark either an X-Y path or a path of the external face + corresponding to: + Case 1. (X=min, ..., B, ..., Y=max) + Case 2. (X=min, ..., B, ..., W=max) + Case 3. (Y=min, ..., B, ..., X=max) + Case 4. (Y=min, ..., B, ..., W=max) */ + + if (theGraph->functions.fpMarkDFSPath(theGraph, max, B) != OK) + return NOTOK; + + gp_SetEdgeVisited(theGraph, B_edge); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, B_edge)); + + /* Delete the unmarked edges in the bicomp. Note that if an unmarked edge + * represents a reduced path, then only the reduction edge is deleted here. + * The path it represents is only deleted later (see NOTE above) */ + + if (_K33Search_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK) + return NOTOK; + + /* Clear all visited flags in the bicomp. + This is the important "step 4" mentioned in the NOTE above */ + + if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + + /* Clear all orientation signs in the bicomp. + Note that the whole bicomp may not be properly oriented at this point + because we may have exchanged external face paths for internal + DFS tree paths. However, the reduced bicomp will be properly + oriented, and the paths of degree 2 vertices will have their + orientations fixed if/when reduction edges are restored. */ + + if (_ClearInvertedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + + /* Reduce the paths to single edges. + Note that although the whole bicomp may not be properly oriented at this + point (as noted above), the four principal vertices R, X, W and Y still + are consistently oriented with one another, e.g. R's link[0] indicates + the external face path toward X that excludes W and Y, and X's link[1] + indicates that same path. */ + + if (_ReduceExternalFacePathToEdge(theGraph, context, R, IC->x, rxType) != OK || + _ReduceExternalFacePathToEdge(theGraph, context, IC->x, IC->w, xwType) != OK || + _ReduceExternalFacePathToEdge(theGraph, context, IC->w, IC->y, wyType) != OK || + _ReduceExternalFacePathToEdge(theGraph, context, IC->y, R, yrType) != OK) + return NOTOK; + + if (_ReduceXYPathToEdge(theGraph, context, IC->x, IC->y, xyType) != OK) + return NOTOK; + + return OK; +} + +/******************************************************************** + Edge deletion that occurs during a reduction or restoration of a + reduction is augmented by clearing the K_{3,3} search-specific + data members. This is augmentation is not needed in the delete edge + operations that happen once a K_{3,3} homeomorph has been found and + marked for isolation. + ********************************************************************/ + +int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink) +{ + _K33Search_InitEdgeRec(context, e); + _K33Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e)); + + return gp_DeleteEdge(theGraph, e, nextLink); +} + +/******************************************************************** + _K33Search_DeleteUnmarkedEdgesInBicomp() + + This function deletes from a given biconnected component all edges + whose visited member is zero. + + The stack is used but preserved. In debug mode, NOTOK can result if + there is a stack overflow. This method pushes at most one integer + per vertex in the bicomp. + + This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls + the overloaded _K33_DeleteEdge() rather than gp_DeleteEdge() + + Returns OK on success, NOTOK on implementation failure + ********************************************************************/ + +int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot) +{ + int V, e; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + + e = gp_GetFirstArc(theGraph, V); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetEdgeVisited(theGraph, e) + ? gp_GetNextArc(theGraph, e) + : _K33Search_DeleteEdge(theGraph, context, e, 0); + } + } + return OK; +} + +/**************************************************************************** + _ReduceExternalFacePathToEdge() + ****************************************************************************/ + +int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType) +{ + int prevLink, v, w, e; + + /* If the path is a single edge, then no need for a reduction */ + + prevLink = 1; + v = _GetNeighborOnExtFace(theGraph, u, &prevLink); + if (v == x) + { + gp_SetExtFaceVertex(theGraph, u, 0, x); + gp_SetExtFaceVertex(theGraph, x, 1, u); + return OK; + } + + /* We have the endpoints u and x of the path, and we just computed the + first vertex internal to the path and a neighbor of u. Now we + compute the vertex internal to the path and a neighbor of x. */ + + prevLink = 0; + w = _GetNeighborOnExtFace(theGraph, x, &prevLink); + + /* Delete the two edges that connect the path to the bicomp. + If either edge is a reduction edge, then we have to restore + the path it represents. We can only afford to visit the + endpoints of the path. + Note that in the restored path, the edge incident to each + endpoint of the original path is a newly added edge, + not a reduction edge. */ + + e = gp_GetFirstArc(theGraph, u); + if (gp_IsVertex(context->E[e].pathConnector)) + { + if (_RestoreReducedPath(theGraph, context, e) != OK) + return NOTOK; + e = gp_GetFirstArc(theGraph, u); + v = gp_GetNeighbor(theGraph, e); + } + _K33Search_DeleteEdge(theGraph, context, e, 0); + + e = gp_GetLastArc(theGraph, x); + if (gp_IsVertex(context->E[e].pathConnector)) + { + if (_RestoreReducedPath(theGraph, context, e) != OK) + return NOTOK; + e = gp_GetLastArc(theGraph, x); + w = gp_GetNeighbor(theGraph, e); + } + _K33Search_DeleteEdge(theGraph, context, e, 0); + + /* Add the reduction edge, then set its path connectors so the original + path can be recovered and set the edge type so the essential structure + of the DFS tree can be maintained (The 'Do X to Bicomp' functions + and functions like MarkDFSPath(0 depend on this). */ + + gp_AddEdge(theGraph, u, 0, x, 1); + + e = gp_GetFirstArc(theGraph, u); + context->E[e].pathConnector = v; + gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType)); + + e = gp_GetLastArc(theGraph, x); + context->E[e].pathConnector = w; + gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType)); + + /* Set the external face info */ + + gp_SetExtFaceVertex(theGraph, u, 0, x); + gp_SetExtFaceVertex(theGraph, x, 1, u); + + return OK; +} + +/**************************************************************************** + _ReduceXYPathToEdge() + ****************************************************************************/ + +int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType) +{ + int e, v, w; + + e = gp_GetFirstArc(theGraph, u); + e = gp_GetNextArc(theGraph, e); + v = gp_GetNeighbor(theGraph, e); + + /* If the XY-path is a single edge, then no reduction is needed */ + + if (v == x) + return OK; + + /* Otherwise, remove the two edges that join the XY-path to the bicomp */ + + if (gp_IsVertex(context->E[e].pathConnector)) + { + if (_RestoreReducedPath(theGraph, context, e) != OK) + return NOTOK; + e = gp_GetFirstArc(theGraph, u); + e = gp_GetNextArc(theGraph, e); + v = gp_GetNeighbor(theGraph, e); + } + _K33Search_DeleteEdge(theGraph, context, e, 0); + + e = gp_GetFirstArc(theGraph, x); + e = gp_GetNextArc(theGraph, e); + w = gp_GetNeighbor(theGraph, e); + if (gp_IsVertex(context->E[e].pathConnector)) + { + if (_RestoreReducedPath(theGraph, context, e) != OK) + return NOTOK; + e = gp_GetFirstArc(theGraph, x); + e = gp_GetNextArc(theGraph, e); + w = gp_GetNeighbor(theGraph, e); + } + _K33Search_DeleteEdge(theGraph, context, e, 0); + + /* Now add a single edge to represent the XY-path */ + gp_InsertEdge(theGraph, u, gp_GetFirstArc(theGraph, u), 0, + x, gp_GetFirstArc(theGraph, x), 0); + + /* Now set up the path connectors so the original XY-path can be recovered if needed. + Also, set the reduction edge's type to preserve the DFS tree structure */ + + e = gp_GetFirstArc(theGraph, u); + e = gp_GetNextArc(theGraph, e); + context->E[e].pathConnector = v; + gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType)); + + e = gp_GetFirstArc(theGraph, x); + e = gp_GetNextArc(theGraph, e); + context->E[e].pathConnector = w; + gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType)); + + return OK; +} + +/**************************************************************************** + _RestoreReducedPath() + Given an edge record of an edge used to reduce a path, we want to restore + the path in constant time. + The path may contain more reduction edges internally, but we do not + search for and process those since it would violate the constant time + bound required of this function. + return OK on success, NOTOK on failure + ****************************************************************************/ + +int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e) +{ + int eTwin, u, v, w, x; + int e0, e1, eTwin0, eTwin1; + + if (gp_IsNotVertex(context->E[e].pathConnector)) + return OK; + + eTwin = gp_GetTwinArc(theGraph, e); + + u = gp_GetNeighbor(theGraph, eTwin); + v = context->E[e].pathConnector; + w = context->E[eTwin].pathConnector; + x = gp_GetNeighbor(theGraph, e); + + /* Get the locations of the edge records between which the new + edge records must be added in order to reconnect the path + parallel to the edge. */ + + e0 = gp_GetNextArc(theGraph, e); + e1 = gp_GetPrevArc(theGraph, e); + eTwin0 = gp_GetNextArc(theGraph, eTwin); + eTwin1 = gp_GetPrevArc(theGraph, eTwin); + + /* We first delete the edge represented by e and eTwin. We do so before + restoring the path to ensure we do not exceed the maximum arc capacity. */ + + _K33Search_DeleteEdge(theGraph, context, e, 0); + + /* Now we add the two edges to reconnect the reduced path represented + by the edge [e, eTwin]. The edge record in u is added between e0 and e1. + Likewise, the new edge record in x is added between eTwin0 and eTwin1. */ + + if (gp_IsArc(e0)) + { + if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) + return NOTOK; + } + else + { + if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) + return NOTOK; + } + + if (gp_IsArc(eTwin0)) + { + if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) + return NOTOK; + } + else + { + if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) + return NOTOK; + } + + // Set the types of the newly added edges. In both cases, the first of the two + // vertex parameters is known to be degree 2 because they are internal to the + // path being restored, so this operation is constant time. + if (_SetEdgeType(theGraph, v, u) != OK || + _SetEdgeType(theGraph, w, x) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _RestoreAndOrientReducedPaths() + This function searches the embedding for any edges that are specially marked + as being representative of a path that was previously reduced to a + single edge by _ReduceBicomp(). The edge is replaced by the path. + Note that the new path may contain more reduction edges, and these will be + iteratively expanded by the outer for loop. + + If the edge records of an edge being expanded are the first or last arcs + of the edge's vertex endpoints, then the edge may be along the external face. + If so, then the vertices along the path being restored must be given a + consistent orientation with the endpoints. It is expected that the embedding + will have been oriented prior to this operation. + ****************************************************************************/ + +int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context) +{ + int EsizeOccupied, e, eTwin, u, v, w, x, visited; + int e0, eTwin0, e1, eTwin1; + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;) + { + if (gp_IsVertex(context->E[e].pathConnector)) + { + visited = gp_GetEdgeVisited(theGraph, e); + + eTwin = gp_GetTwinArc(theGraph, e); + u = gp_GetNeighbor(theGraph, eTwin); + v = context->E[e].pathConnector; + w = context->E[eTwin].pathConnector; + x = gp_GetNeighbor(theGraph, e); + + /* Now we need the predecessor and successor edge records + of e and eTwin. The edge (u, v) will be inserted so + that the record in u's adjacency list that indicates v + will be between e0 and e1. Likewise, the edge record + (x -> w) will be placed between eTwin0 and eTwin1. */ + + e0 = gp_GetNextArc(theGraph, e); + e1 = gp_GetPrevArc(theGraph, e); + eTwin0 = gp_GetNextArc(theGraph, eTwin); + eTwin1 = gp_GetPrevArc(theGraph, eTwin); + + /* We first delete the edge represented by e and eTwin. We do so before + restoring the path to ensure we do not exceed the maximum arc capacity. */ + + _K33Search_DeleteEdge(theGraph, context, e, 0); + + /* Now we add the two edges to reconnect the reduced path represented + by the edge [e, eTwin]. The edge record in u is added between e0 and e1. + Likewise, the new edge record in x is added between eTwin0 and eTwin1. */ + + if (gp_IsArc(e0)) + { + if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) + return NOTOK; + } + else + { + if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) + return NOTOK; + } + + if (gp_IsArc(eTwin0)) + { + if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) + return NOTOK; + } + else + { + if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) + return NOTOK; + } + + /* Set the types of the newly added edges */ + + if (_SetEdgeType(theGraph, u, v) != OK || + _SetEdgeType(theGraph, w, x) != OK) + return NOTOK; + + /* We determine whether the reduction edge may be on the external face, + in which case we will need to ensure that the vertices on the path + being restored are consistently oriented. This will accommodate + future invocations of MarkPathAlongBicompExtFace(). + Note: If e0, e1, eTwin0 or eTwin1 is not an edge, then it is + because we've walked off the end of the edge record list, + which happens when e and eTwin are either the first or + last edge of the containing vertex. In turn, the first + and last edges of a vertex are the ones that hold it onto + the external face, if it is on the external face. */ + + if ((gp_IsNotArc(e0) && gp_IsNotArc(eTwin1)) || (gp_IsNotArc(e1) && gp_IsNotArc(eTwin0))) + { + if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK) + return NOTOK; + } + + /* The internal XY path was already marked as part of the decision logic + that made us decide we could find a K_{3,3} and hence that we should + reverse all of the reductions. Subsequent code counts on the fact + that the X-Y path is already marked, so if we replace a marked edge + with a path, then we need to mark the path. Similarly, for an unmarked + edge, the replacement path should be unmarked. */ + + if (visited) + { + if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) + return NOTOK; + } + else + { + if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) + return NOTOK; + } + } + else + e += 2; + } + + return OK; +} + +/**************************************************************************** + _MarkStraddlingBridgePath() + ****************************************************************************/ + +int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d) +{ + isolatorContextP IC = &theGraph->IC; + int p, e; + + /* Find the point of intersection p between the path (v ... u_max) + and the path (d ... u_max). */ + + if (theGraph->functions.fpMarkDFSPath(theGraph, u_max, IC->r) != OK) + return NOTOK; + + p = d; + while (!gp_GetVertexVisited(theGraph, p)) + { + gp_SetVertexVisited(theGraph, p); + + e = gp_GetFirstArc(theGraph, p); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) + break; + + e = gp_GetNextArc(theGraph, e); + } + + gp_SetEdgeVisited(theGraph, e); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + + p = gp_GetNeighbor(theGraph, e); + + /* If p is a root copy, mark it visited and skip to the parent copy */ + if (gp_IsVirtualVertex(theGraph, p)) + { + gp_SetVertexVisited(theGraph, p); + p = gp_GetPrimaryVertexFromRoot(theGraph, p); + } + } + + /* Unmark the path (p ... u_max), which was marked to help find p. + The path from v to u_{max} is not needed to form a K_{3,3} except + for the portion of the path up to p that, with the straddling + bridge path, comprises part of the connection to u_d. In the + minor, the path between v and p is edge contracted. */ + + while (p != u_max) + { + e = gp_GetFirstArc(theGraph, p); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) + break; + + e = gp_GetNextArc(theGraph, e); + } + + gp_ClearEdgeVisited(theGraph, e); + gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + + p = gp_GetNeighbor(theGraph, e); + gp_ClearVertexVisited(theGraph, p); + + /* If p is a root copy, clear its visited flag and skip to the + parent copy */ + + if (gp_IsVirtualVertex(theGraph, p)) + { + p = gp_GetPrimaryVertexFromRoot(theGraph, p); + gp_ClearVertexVisited(theGraph, p); + } + } + + /* The straddling bridge must join the path (u_max ... u_min). If u_d is an + ancestor of u_min, then mark the path that joins u_d to u_min. */ + + if (u_d < u_min) + if (theGraph->functions.fpMarkDFSPath(theGraph, u_d, u_min) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorE5() + The paths (x, w), (y, w) and (v, u_{max}) are not needed. + The x-y path and the internal w-z path are already marked. + ****************************************************************************/ + +int _IsolateMinorE5(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), + MAX3(IC->ux, IC->uy, IC->uz)) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorE6() + The paths (x, y), (v, w) and (v, u_{max}) are not needed. + The path through the straddling bridge that connects from an ancestor of + u_{max} to v is required, but it may connnect to an ancestor p of v. + In such a case, the path (v, p) is required, while (p, u_{max}) is not. + ****************************************************************************/ + +int _IsolateMinorE6(graphP theGraph, K33SearchContext *context) +{ + isolatorContextP IC = &theGraph->IC; + int u_min, u_max, d, u_d; + + /* Clear the previously marked x-y path */ + + if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) + return NOTOK; + + /* Clear dw to stop the marking of path (v, w) */ + + IC->dw = NIL; + + /* Mark (v, ..., x, ..., w, ..., y, ... v) */ + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK) + return NOTOK; + + /* Mark the path through the straddling bridge (except for the final + edge (u_d, d) which is added last by convention). */ + + u_min = MIN3(IC->ux, IC->uy, IC->uz); + u_max = MAX3(IC->ux, IC->uy, IC->uz); + d = _TestForStraddlingBridge(theGraph, context, u_max); + u_d = gp_GetVertexLeastAncestor(theGraph, d); + + if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK) + return NOTOK; + + /* Make the final markings and edge additions */ + + if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK || + _AddAndMarkEdge(theGraph, u_d, d) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorE7() + ****************************************************************************/ + +int _IsolateMinorE7(graphP theGraph, K33SearchContext *context) +{ + isolatorContextP IC = &theGraph->IC; + int u_min, u_max, d, u_d; + + /* Mark the appropriate two portions of the external face depending on + symmetry condition */ + + if (IC->uy < IC->ux) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) + return NOTOK; + } + else + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK) + return NOTOK; + } + + /* Mark the path through the straddling bridge (except for the final + edge (u_d, d) which is added last by convention). */ + + u_min = MIN3(IC->ux, IC->uy, IC->uz); + u_max = MAX3(IC->ux, IC->uy, IC->uz); + d = _TestForStraddlingBridge(theGraph, context, u_max); + u_d = gp_GetVertexLeastAncestor(theGraph, d); + + if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK) + return NOTOK; + + /* Make the final markings and edge additions */ + + if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK || + _AddAndMarkEdge(theGraph, u_d, d) != OK) + return NOTOK; + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.h new file mode 100644 index 000000000..074a07650 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_K33SEARCH_H +#define GRAPH_K33SEARCH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graphStructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define K33SEARCH_NAME "K33Search" + + int gp_AttachK33Search(graphP theGraph); + int gp_DetachK33Search(graphP theGraph); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.private.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.private.h new file mode 100644 index 000000000..36c36ff83 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.private.h @@ -0,0 +1,62 @@ +#ifndef GRAPH_K33SEARCH_PRIVATE_H +#define GRAPH_K33SEARCH_PRIVATE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graph.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + // Additional equipment for each EdgeRec + typedef struct + { + int noStraddle, pathConnector; + } K33Search_EdgeRec; + + typedef K33Search_EdgeRec *K33Search_EdgeRecP; + + // Additional equipment for each primary vertex + typedef struct + { + int separatedDFSChildList, backArcList, mergeBlocker; + } K33Search_VertexInfo; + + typedef K33Search_VertexInfo *K33Search_VertexInfoP; + + typedef struct + { + // Helps distinguish initialize from re-initialize + int initialized; + + // The graph that this context augments + graphP theGraph; + + // Parallel array for additional edge level equipment + K33Search_EdgeRecP E; + + // Parallel array for additional vertex info level equipment + K33Search_VertexInfoP VI; + + // Storage for the separatedDFSChildLists, and + // to help with linear time sorting of same by lowpoints + listCollectionP separatedDFSChildLists; + int *buckets; + listCollectionP bin; + + // Overloaded function pointers + graphFunctionTable functions; + + } K33SearchContext; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c new file mode 100644 index 000000000..f61bd964b --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search_Extensions.c @@ -0,0 +1,751 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "graphK33Search.private.h" +#include "graphK33Search.h" + +extern int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker); +extern int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker); +extern int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R); + +extern int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts); +extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, + int *imageVerts, int maxNumImageVerts); +extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); + +/* Forward declarations of local functions */ + +void _K33Search_ClearStructures(K33SearchContext *context); +int _K33Search_CreateStructures(K33SearchContext *context); +int _K33Search_InitStructures(K33SearchContext *context); + +void _K33Search_InitEdgeRec(K33SearchContext *context, int e); +void _K33Search_InitVertexInfo(K33SearchContext *context, int v); + +/* Forward declarations of overloading functions */ + +int _K33Search_EmbeddingInitialize(graphP theGraph); +void _CreateBackArcLists(graphP theGraph, K33SearchContext *context); +void _CreateSeparatedDFSChildLists(graphP theGraph, K33SearchContext *context); +void _K33Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); +int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); +void _K33Search_MergeVertex(graphP theGraph, int W, int WPrevLink, int R); +int _K33Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); +int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); +int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); +int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); + +int _K33Search_InitGraph(graphP theGraph, int N); +void _K33Search_ReinitializeGraph(graphP theGraph); +int _K33Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); + +/* Forward declarations of functions used by the extension system */ + +void *_K33Search_DupContext(void *pContext, void *theGraph); +void _K33Search_FreeContext(void *); + +/**************************************************************************** + * K33SEARCH_ID - the variable used to hold the integer identifier for this + * extension, enabling this feature's extension context to be distinguished + * from other features' extension contexts that may be attached to a graph. + ****************************************************************************/ + +int K33SEARCH_ID = 0; + +/**************************************************************************** + gp_AttachK33Search() + + This function adjusts the graph data structure to attach the K3,3 search + feature. + ****************************************************************************/ + +int gp_AttachK33Search(graphP theGraph) +{ + K33SearchContext *context = NULL; + + // If the K3,3 search feature has already been attached to the graph, + // then there is no need to attach it again + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + if (context != NULL) + { + return OK; + } + + // Allocate a new extension context + context = (K33SearchContext *)malloc(sizeof(K33SearchContext)); + if (context == NULL) + { + return NOTOK; + } + + // First, tell the context that it is not initialized + context->initialized = 0; + + // Save a pointer to theGraph in the context + context->theGraph = theGraph; + + // Put the overload functions into the context function table. + // gp_AddExtension will overload the graph's functions with these, and + // return the base function pointers in the context function table + memset(&context->functions, 0, sizeof(graphFunctionTable)); + + context->functions.fpEmbeddingInitialize = _K33Search_EmbeddingInitialize; + context->functions.fpEmbedBackEdgeToDescendant = _K33Search_EmbedBackEdgeToDescendant; + context->functions.fpMergeBicomps = _K33Search_MergeBicomps; + context->functions.fpMergeVertex = _K33Search_MergeVertex; + context->functions.fpHandleBlockedBicomp = _K33Search_HandleBlockedBicomp; + context->functions.fpEmbedPostprocess = _K33Search_EmbedPostprocess; + context->functions.fpCheckEmbeddingIntegrity = _K33Search_CheckEmbeddingIntegrity; + context->functions.fpCheckObstructionIntegrity = _K33Search_CheckObstructionIntegrity; + + context->functions.fpInitGraph = _K33Search_InitGraph; + context->functions.fpReinitializeGraph = _K33Search_ReinitializeGraph; + context->functions.fpEnsureArcCapacity = _K33Search_EnsureArcCapacity; + + _K33Search_ClearStructures(context); + + // Store the K33 search context, including the data structure and the + // function pointers, as an extension of the graph + if (gp_AddExtension(theGraph, &K33SEARCH_ID, (void *)context, + _K33Search_DupContext, _K33Search_FreeContext, + &context->functions) != OK) + { + _K33Search_FreeContext(context); + return NOTOK; + } + + // Create the K33-specific structures if the size of the graph is known + // Attach functions are always invoked after gp_New(), but if a graph + // extension must be attached before gp_Read(), then the attachment + // also happens before gp_InitGraph(), which means N==0. + // However, sometimes a feature is attached after gp_InitGraph(), in + // which case N > 0 + if (theGraph->N > 0) + { + if (_K33Search_CreateStructures(context) != OK || + _K33Search_InitStructures(context) != OK) + { + _K33Search_FreeContext(context); + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + gp_DetachK33Search() + ********************************************************************/ + +int gp_DetachK33Search(graphP theGraph) +{ + return gp_RemoveExtension(theGraph, K33SEARCH_ID); +} + +/******************************************************************** + _K33Search_ClearStructures() + ********************************************************************/ + +void _K33Search_ClearStructures(K33SearchContext *context) +{ + if (!context->initialized) + { + // Before initialization, the pointers are stray, not NULL + // Once NULL or allocated, free() or LCFree() can do the job + context->E = NULL; + context->VI = NULL; + + context->separatedDFSChildLists = NULL; + context->buckets = NULL; + context->bin = NULL; + + context->initialized = 1; + } + else + { + if (context->E != NULL) + { + free(context->E); + context->E = NULL; + } + if (context->VI != NULL) + { + free(context->VI); + context->VI = NULL; + } + + LCFree(&context->separatedDFSChildLists); + if (context->buckets != NULL) + { + free(context->buckets); + context->buckets = NULL; + } + LCFree(&context->bin); + } +} + +/******************************************************************** + _K33Search_CreateStructures() + Create uninitialized structures for the vertex and edge + levels, and initialized structures for the graph level + ********************************************************************/ +int _K33Search_CreateStructures(K33SearchContext *context) +{ + int VIsize = gp_PrimaryVertexIndexBound(context->theGraph); + int Esize = gp_EdgeIndexBound(context->theGraph); + + if (context->theGraph->N <= 0) + return NOTOK; + + if ((context->E = (K33Search_EdgeRecP)malloc(Esize * sizeof(K33Search_EdgeRec))) == NULL || + (context->VI = (K33Search_VertexInfoP)malloc(VIsize * sizeof(K33Search_VertexInfo))) == NULL || + (context->separatedDFSChildLists = LCNew(VIsize)) == NULL || + (context->buckets = (int *)malloc(VIsize * sizeof(int))) == NULL || + (context->bin = LCNew(VIsize)) == NULL) + { + return NOTOK; + } + + return OK; +} + +/******************************************************************** + _K33Search_InitStructures() + ********************************************************************/ +int _K33Search_InitStructures(K33SearchContext *context) +{ + memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(K33Search_VertexInfo)); + memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K33Search_EdgeRec)); + // N.B. This is the legacy API-based approach to initializing the structures + // required for the K_{3, 3} search graph algorithm extension. + // graphP theGraph = context->theGraph; + // int v, e, Esize; + + // if (theGraph->N <= 0) + // return OK; + + // for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + // _K33Search_InitVertexInfo(context, v); + + // Esize = gp_EdgeIndexBound(theGraph); + // for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) + // _K33Search_InitEdgeRec(context, e); + + return OK; +} + +/******************************************************************** + ********************************************************************/ + +int _K33Search_InitGraph(graphP theGraph, int N) +{ + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context == NULL) + { + return NOTOK; + } + + theGraph->N = N; + theGraph->NV = N; + if (theGraph->arcCapacity == 0) + theGraph->arcCapacity = 2 * DEFAULT_EDGE_LIMIT * N; + + if (_K33Search_CreateStructures(context) != OK || + _K33Search_InitStructures(context) != OK) + return NOTOK; + + context->functions.fpInitGraph(theGraph, N); + + return OK; +} + +/******************************************************************** + ********************************************************************/ + +void _K33Search_ReinitializeGraph(graphP theGraph) +{ + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + // Reinitialize the graph + context->functions.fpReinitializeGraph(theGraph); + + // Do the reinitialization that is specific to this module + _K33Search_InitStructures(context); + LCReset(context->separatedDFSChildLists); + LCReset(context->bin); + } +} + +/******************************************************************** + The current implementation does not support an increase of arc + (edge record) capacity once the extension is attached to the graph + data structure. This is only due to not being necessary to support. + For now, it is easy to ensure the correct capacity before attaching + the extension, but support could be added later if there is some + reason to do so. + ********************************************************************/ + +int _K33Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) +{ + return NOTOK; +} + +/******************************************************************** + _K33Search_DupContext() + ********************************************************************/ + +void *_K33Search_DupContext(void *pContext, void *theGraph) +{ + K33SearchContext *context = (K33SearchContext *)pContext; + K33SearchContext *newContext = (K33SearchContext *)malloc(sizeof(K33SearchContext)); + + if (newContext != NULL) + { + int VIsize = gp_PrimaryVertexIndexBound((graphP)theGraph); + int Esize = gp_EdgeIndexBound((graphP)theGraph); + + *newContext = *context; + + newContext->theGraph = (graphP)theGraph; + + newContext->initialized = 0; + _K33Search_ClearStructures(newContext); + if (((graphP)theGraph)->N > 0) + { + if (_K33Search_CreateStructures(newContext) != OK) + { + _K33Search_FreeContext(newContext); + return NULL; + } + + memcpy(newContext->E, context->E, Esize * sizeof(K33Search_EdgeRec)); + memcpy(newContext->VI, context->VI, VIsize * sizeof(K33Search_VertexInfo)); + LCCopy(newContext->separatedDFSChildLists, context->separatedDFSChildLists); + } + } + + return newContext; +} + +/******************************************************************** + _K33Search_FreeContext() + ********************************************************************/ + +void _K33Search_FreeContext(void *pContext) +{ + K33SearchContext *context = (K33SearchContext *)pContext; + + _K33Search_ClearStructures(context); + free(pContext); +} + +/******************************************************************** + _K33Search_EmbeddingInitialize() + + This method overloads the embedding initialization phase of the + core planarity algorithm to provide post-processing that creates + the back arcs list and separated DFS child list (sorted by + lowpoint) for each vertex. + ********************************************************************/ + +int _K33Search_EmbeddingInitialize(graphP theGraph) +{ + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + if (context->functions.fpEmbeddingInitialize(theGraph) != OK) + return NOTOK; + + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + _CreateBackArcLists(theGraph, context); + _CreateSeparatedDFSChildLists(theGraph, context); + } + + return OK; + } + + return NOTOK; +} + +/******************************************************************** + _CreateBackArcLists() + ********************************************************************/ +void _CreateBackArcLists(graphP theGraph, K33SearchContext *context) +{ + int v, e, eTwin, ancestor; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + e = gp_GetVertexFwdArcList(theGraph, v); + while (gp_IsArc(e)) + { + // Get the ancestor endpoint and the associated back arc + ancestor = gp_GetNeighbor(theGraph, e); + eTwin = gp_GetTwinArc(theGraph, e); + + // Put it into the back arc list of the ancestor + if (gp_IsNotArc(context->VI[ancestor].backArcList)) + { + context->VI[ancestor].backArcList = eTwin; + gp_SetPrevArc(theGraph, eTwin, eTwin); + gp_SetNextArc(theGraph, eTwin, eTwin); + } + else + { + int eHead = context->VI[ancestor].backArcList; + int eTail = gp_GetPrevArc(theGraph, eHead); + gp_SetPrevArc(theGraph, eTwin, eTail); + gp_SetNextArc(theGraph, eTwin, eHead); + gp_SetPrevArc(theGraph, eHead, eTwin); + gp_SetNextArc(theGraph, eTail, eTwin); + } + + // Advance to the next forward edge + e = gp_GetNextArc(theGraph, e); + if (e == gp_GetVertexFwdArcList(theGraph, v)) + e = NIL; + } + } +} + +/******************************************************************** + _CreateSeparatedDFSChildLists() + + Each vertex gets a list of its DFS children, sorted by lowpoint. + ********************************************************************/ + +void _CreateSeparatedDFSChildLists(graphP theGraph, K33SearchContext *context) +{ + int *buckets; + listCollectionP bin; + int v, L, DFSParent, theList; + + buckets = context->buckets; + bin = context->bin; + + // Initialize the bin and all the buckets to be empty + LCReset(bin); + for (L = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, L); L++) + buckets[L] = NIL; + + // For each vertex, add it to the bucket whose index is equal to the lowpoint of the vertex. + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + L = gp_GetVertexLowpoint(theGraph, v); + buckets[L] = LCAppend(bin, buckets[L], v); + } + + // For each bucket, add each vertex in the bucket to the separatedDFSChildList of its DFSParent. + // Since lower numbered buckets are processed before higher numbered buckets, vertices with lower + // lowpoint values are added before those with higher lowpoint values, so the separatedDFSChildList + // of each vertex is sorted by lowpoint + for (L = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, L); L++) + { + v = buckets[L]; + + // Loop through all the vertices with lowpoint L, putting each in the list of its parent + while (gp_IsVertex(v)) + { + DFSParent = gp_GetVertexParent(theGraph, v); + + if (gp_IsVertex(DFSParent) && DFSParent != v) + { + theList = context->VI[DFSParent].separatedDFSChildList; + theList = LCAppend(context->separatedDFSChildLists, theList, v); + context->VI[DFSParent].separatedDFSChildList = theList; + } + + v = LCGetNext(bin, buckets[L], v); + } + } +} + +/******************************************************************** + _K33Search_EmbedBackEdgeToDescendant() + + The forward and back arcs of the cycle edge are embedded by the planarity + version of this function. + However, for K_{3,3} subgraph homeomorphism, we also maintain the + list of unembedded back arcs, so we need to remove the back arc from + that list since it is now being put back into the adjacency list. + ********************************************************************/ + +void _K33Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink) +{ + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + // K33 search may have been attached, but not enabled + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + // Get the fwdArc from the adjacentTo field, and use it to get the backArc + int backArc = gp_GetTwinArc(theGraph, gp_GetVertexPertinentEdge(theGraph, W)); + + // Remove the backArc from the backArcList + if (context->VI[W].backArcList == backArc) + { + if (gp_GetNextArc(theGraph, backArc) == backArc) + context->VI[W].backArcList = NIL; + else + context->VI[W].backArcList = gp_GetNextArc(theGraph, backArc); + } + + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, backArc), gp_GetNextArc(theGraph, backArc)); + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, backArc), gp_GetPrevArc(theGraph, backArc)); + } + + // Invoke the superclass version of the function + context->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink); + } +} + +/******************************************************************** + This override of _MergeBicomps() detects a special merge block + that indicates a K3,3 can be found. The merge blocker is an + optimization needed for one case for which detecting a K3,3 + could not be done in linear time by direct searching of a + path of ancestors that is naturally explored eventually by + the core planarity algorithm. + + Returns OK for a successful merge, NOTOK on an internal failure, + or NONEMBEDDABLE if the merge was blocked, in which case + a K_{3,3} homeomorph was isolated. + ********************************************************************/ + +int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink) +{ + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + /* If the merge is blocked, then a K_{3,3} homeomorph is isolated, + and NONEMBEDDABLE is returned so that the Walkdown terminates */ + + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + int mergeBlocker; + + // We want to test all merge points on the stack + // as well as W, since the connection will go + // from W. So we push W as a 'degenerate' merge point. + sp_Push2(theGraph->theStack, W, WPrevLink); + sp_Push2(theGraph->theStack, NIL, NIL); + + if (_SearchForMergeBlocker(theGraph, context, v, &mergeBlocker) != OK) + return NOTOK; + + if (gp_IsVertex(mergeBlocker)) + { + if (_FindK33WithMergeBlocker(theGraph, context, v, mergeBlocker) != OK) + return NOTOK; + + return NONEMBEDDABLE; + } + + // If no merge blocker was found, then remove W from the stack. + sp_Pop2(theGraph->theStack, W, WPrevLink); + sp_Pop2(theGraph->theStack, W, WPrevLink); + } + + // If the merge was not blocked, then we perform the merge + // When not doing a K3,3 search, then the merge is not + // blocked as far as the K3,3 search method is concerned + // Another algorithms could overload MergeBicomps and block + // merges under certain conditions, but those would be based + // on data maintained by the extension that implements the + // other algorithm-- if *that* algorithm is the one being run + return context->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink); + } + + return NOTOK; +} + +/******************************************************************** + _K33Search_MergeVertex() + + Overload of merge vertex that does basic behavior but also removes + the DFS child associated with R from the separatedDFSChildList of W. + ********************************************************************/ +void _K33Search_MergeVertex(graphP theGraph, int W, int WPrevLink, int R) +{ + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + int theList = context->VI[W].separatedDFSChildList; + theList = LCDelete(context->separatedDFSChildLists, theList, gp_GetDFSChildFromRoot(theGraph, R)); + context->VI[W].separatedDFSChildList = theList; + } + + context->functions.fpMergeVertex(theGraph, W, WPrevLink, R); + } +} + +/******************************************************************** + ********************************************************************/ + +void _K33Search_InitEdgeRec(K33SearchContext *context, int e) +{ + context->E[e].noStraddle = NIL; + context->E[e].pathConnector = NIL; +} + +/******************************************************************** + ********************************************************************/ + +void _K33Search_InitVertexInfo(K33SearchContext *context, int v) +{ + context->VI[v].separatedDFSChildList = NIL; + context->VI[v].backArcList = NIL; + context->VI[v].mergeBlocker = NIL; +} + +/******************************************************************** + ********************************************************************/ + +int _K33Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) +{ + K33SearchContext *context = NULL; + + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + if (context == NULL) + return NOTOK; + + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + // If R is the root of a descendant bicomp of v, we push it, but then we know the search for K3,3 + // will be successful and return NONEMBEDDABLE because this condition corresponds to minor A, which + // is a K3,3. Thus, an "OK to proceed with Walkdown searching elsewhere" result cannot happen, + // so we don't have to test for it to detect if we have to pop these two back off the stack. + if (R != RootVertex) + sp_Push2(theGraph->theStack, R, 0); + + // The possible results here are NONEMBEDDABLE if a K3,3 homeomorph is found, or OK if only + // a K5 was found and unblocked such that it is OK for the Walkdown to continue searching + // elsewhere. Note that the OK result can only happen if RootVertex==R since minor E can only + // happen on a child bicomp of vertex v, not a descendant bicomp. + return _SearchForK33InBicomp(theGraph, context, v, RootVertex); + } + else + { + return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + // For K3,3 search, we just return the edge embedding result because the + // search result has been obtained already. + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + return edgeEmbeddingResult; + } + + // When not searching for K3,3, we let the superclass do the work + else + { + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) +{ + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + return OK; + } + + // When not searching for K3,3, we let the superclass do the work + else + { + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) +{ + // When searching for K3,3, we ensure that theGraph is a subgraph of + // the original graph and that it contains a K3,3 homeomorph + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + int degrees[5], imageVerts[6]; + + if (_TestSubgraph(theGraph, origGraph) != TRUE) + { + return NOTOK; + } + + if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK) + { + return NOTOK; + } + + if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE) + { + return OK; + } + + return NOTOK; + } + + // When not searching for K3,3, we let the superclass do the work + else + { + K33SearchContext *context = NULL; + gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); + } + } + + return NOTOK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c new file mode 100644 index 000000000..396229edd --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c @@ -0,0 +1,1490 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "graphK4Search.h" +#include "graphK4Search.private.h" + +extern int K4SEARCH_ID; + +#include "../graph.h" + +/* Imported functions */ + +extern void _InitIsolatorContext(graphP theGraph); +extern void _ClearVisitedFlags(graphP); +extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); +// extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); +// extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); +extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); +// extern int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot); +extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); +extern int _SetEdgeType(graphP theGraph, int u, int v); + +extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); +extern int _JoinBicomps(graphP theGraph); +// extern void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY); +extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); +extern int _OrientVerticesInEmbedding(graphP theGraph); +// extern void _InvertVertex(graphP theGraph, int V); +extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); + +extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); +extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); +extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); + +extern int _SetVertexTypesForMarkingXYPath(graphP theGraph); +extern int _MarkHighestXYPath(graphP theGraph); +extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); +extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); +extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); + +extern int _IsolateOuterplanarityObstructionA(graphP theGraph); +// extern int _IsolateOuterplanarityObstructionB(graphP theGraph); +extern int _IsolateOuterplanarityObstructionE(graphP theGraph); + +extern void _K4Search_InitEdgeRec(K4SearchContext *context, int e); + +/* Private functions for K4 searching (exposed to the extension). */ + +int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R); + +/* Private functions for K4 searching. */ + +int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); + +int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph); +int _K4_FindPlanarityActiveVertex(graphP theGraph, int v, int R, int prevLink, int *pW); +int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY); +void _K4_MarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A); +void _K4_UnmarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A); + +int _K4_IsolateMinorA1(graphP theGraph); +int _K4_IsolateMinorA2(graphP theGraph); +int _K4_IsolateMinorB1(graphP theGraph); +int _K4_IsolateMinorB2(graphP theGraph); + +int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W); +int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A); +int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A); + +int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant); +int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A); +void _K4_ClearVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A); +int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A); +int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, int BicompRoot); + +int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e); +int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context); + +// int _MarkEdge(graphP theGraph, int x, int y); + +/**************************************************************************** + _SearchForK4InBicomp() + ****************************************************************************/ + +int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R) +{ + isolatorContextP IC = &theGraph->IC; + + if (context == NULL) + { + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + if (context == NULL) + return NOTOK; + } + + // Begin by determining whether minor A, B or E is detected + if (_K4_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) + return NOTOK; + + // Minor A indicates the existence of K_{2,3} homeomorphs, but + // we run additional tests to see whether we can either find an + // entwined K4 homeomorph or reduce the bicomp so that the WalkDown + // is enabled to continue to resolve pertinence + if (theGraph->IC.minorType & MINORTYPE_A) + { + // Now that we know we have minor A, we can afford to orient the + // bicomp because we will either find the desired K4 or we will + // reduce the bicomp to an edge. The tests for A1 and A2 are easier + // to implement on an oriented bicomp. + // NOTE: We're in the midst of the WalkDown, so the stack may be + // non-empty, and it has to be preserved with constant cost. + // The stack will have at most 4 integers per cut vertex + // merge point, and this operation will push at most two + // integers per tree edge in the bicomp, so the stack + // will not overflow. + if (sp_GetCapacity(theGraph->theStack) < 6 * theGraph->N) + return NOTOK; + + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) + return NOTOK; + + // Case A1: Test whether there is an active vertex Z other than W + // along the external face path [X, ..., W, ..., Y] + if (_K4_FindSecondActiveVertexOnLowExtFacePath(theGraph) == TRUE) + { + // Now that we know we can find a K4, the Walkdown will not continue + // and we can do away with the stack content. + sp_ClearStack(theGraph->theStack); + + // Restore the orientations of the vertices in the bicomp, then orient + // the whole embedding, so we can restore and orient the reduced paths + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK || + _OrientVerticesInEmbedding(theGraph) != OK || + _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + // Set up to isolate K4 homeomorph + _ClearVisitedFlags(theGraph); + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + + if (IC->uz < IC->v) + { + if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE) + return NOTOK; + } + else + { + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->z, &IC->dz) != TRUE) + return NOTOK; + } + + // Isolate the K4 homeomorph + if (_K4_IsolateMinorA1(theGraph) != OK || + _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + // Indicate success by returning NONEMBEDDABLE + return NONEMBEDDABLE; + } + + // Case A2: Test whether the bicomp has an XY path + // NOTE: As mentioned above, the stack is also preserved here. + // It will have at most 4 integers per cut vertex merge point, + // and this operation will push at most one integer per tree + // edge in the bicomp, so the stack will not overflow. + if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) + return NOTOK; + + // Marking the X-Y path relies on the bicomp visited flags being cleared + if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + + // Now Mark the X-Y path + // NOTE: This call preserves the stack and does not overflow. There + // are at most 4 integers per cut vertex merge point, all of which + // are not in the bicomp, and this call pushes at most 3 integers + // per bicomp vertex, so the maximum stack requirement is 4N + if (_MarkHighestXYPath(theGraph) != OK) + return NOTOK; + + // If there was an X-Y path to mark... + if (theGraph->IC.py != NIL) + { + // Now that we know we can find a K4, the Walkdown will not continue + // and we can do away with the stack content. + sp_ClearStack(theGraph->theStack); + + // Restore the orientations of the vertices in the bicomp, then orient + // the whole embedding, so we can restore and orient the reduced paths + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK || + _OrientVerticesInEmbedding(theGraph) != OK || + _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + // Set up to isolate K4 homeomorph + _ClearVisitedFlags(theGraph); + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + { + return NOTOK; + } + + // Fail if there is an internal error or if there isn't an X-Y path + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + + // Isolate the K4 homeomorph + if (_K4_IsolateMinorA2(theGraph) != OK || + _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + // Indicate success by returning NONEMBEDDABLE + return NONEMBEDDABLE; + } + + // else if there was no X-Y path, then we restore the vertex types to + // unknown (though it would suffice to do it just to R and W) + if (_ClearVertexTypeInBicomp(theGraph, R) != OK) + return NOTOK; + + // Since neither A1 nor A2 is found, then we reduce the bicomp to the + // tree edge (R, W). + // NOTE: The visited flags for R and W are restored to values appropriate + // for continuing with future embedding steps + // NOTE: This method invokes several routines that use the stack, but + // all of them preserve the stack and each pushes at most one + // integer per bicomp vertex and pops all of them before returning. + // Again, this means the stack will not overflow. + if (_K4_ReduceBicompToEdge(theGraph, context, R, IC->w) != OK) + return NOTOK; + + // Return OK so that the WalkDown can continue resolving the pertinence of v. + return OK; + } + + // Minor B also indicates the existence of K_{2,3} homeomorphs, but + // we run additional tests to see whether we can either find an + // entwined K4 homeomorph or reduce a portion of the bicomp so that + // the WalkDown can be reinvoked on the bicomp + else if (theGraph->IC.minorType & MINORTYPE_B) + { + int a_x, a_y; + + // Reality check on stack state + if (sp_NonEmpty(theGraph->theStack)) + return NOTOK; + + // Find the vertices a_x and a_y that are active (pertinent or future pertinent) + // and also first along the external face paths emanating from the bicomp root + if (_K4_FindPlanarityActiveVertex(theGraph, v, R, 1, &a_x) != OK || + _K4_FindPlanarityActiveVertex(theGraph, v, R, 0, &a_y) != OK) + return NOTOK; + + // Case B1: If both a_x and a_y are future pertinent, then we can stop and + // isolate a subgraph homeomorphic to K4. + gp_UpdateVertexFuturePertinentChild(theGraph, a_x, v); + gp_UpdateVertexFuturePertinentChild(theGraph, a_y, v); + if (a_x != a_y && FUTUREPERTINENT(theGraph, a_x, v) && FUTUREPERTINENT(theGraph, a_y, v)) + { + if (_OrientVerticesInEmbedding(theGraph) != OK || + _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + // Set up to isolate K4 homeomorph + _ClearVisitedFlags(theGraph); + + IC->x = a_x; + IC->y = a_y; + + if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE || + _FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE) + return NOTOK; + + // Isolate the K4 homeomorph + if (_K4_IsolateMinorB1(theGraph) != OK || + _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + // Indicate success by returning NONEMBEDDABLE + return NONEMBEDDABLE; + } + + // Reality check: The bicomp with root R is pertinent, and the only + // pertinent or future pertinent vertex on the external face is a_x, + // so it must also be pertinent. + if (a_x == a_y && !PERTINENT(theGraph, a_x)) + return NOTOK; + + // Case B2: Determine whether there is an internal separating X-Y path for a_x or for a_y + // The method makes appropriate isolator context settings if the separator edge is found + if (_K4_FindSeparatingInternalEdge(theGraph, R, 1, a_x, &IC->w, &IC->px, &IC->py) == TRUE || + _K4_FindSeparatingInternalEdge(theGraph, R, 0, a_y, &IC->w, &IC->py, &IC->px) == TRUE) + { + if (_OrientVerticesInEmbedding(theGraph) != OK || + _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + // Set up to isolate K4 homeomorph + _ClearVisitedFlags(theGraph); + + if (PERTINENT(theGraph, IC->w)) + { + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + } + else + { + IC->z = IC->w; + if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE) + return NOTOK; + } + + // The X-Y path doesn't have to be the same one that was associated with + // the separating internal edge (but it has to be there, else error). + if (_SetVertexTypesForMarkingXYPath(theGraph) != OK || + _MarkHighestXYPath(theGraph) != OK || + theGraph->IC.py == NIL) + return NOTOK; + + // Isolate the K4 homeomorph + if (_K4_IsolateMinorB2(theGraph) != OK || + _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + // Indicate success by returning NONEMBEDDABLE + return NONEMBEDDABLE; + } + + // If K_4 homeomorph not found, make reductions along a_x and a_y paths. + if (a_x == a_y) + { + // In the special case where both paths lead to the same vertex, we can + // reduce the bicomp to a single edge, which avoids issues of reversed + // orientation between the bicomp root and the vertex. + if (_K4_ReduceBicompToEdge(theGraph, context, R, a_x) != OK) + return NOTOK; + } + else + { + // When a_x and a_y are distinct, we reduce each path from root to the vertex + if (_K4_ReducePathComponent(theGraph, context, R, 1, a_x) != OK || + _K4_ReducePathComponent(theGraph, context, R, 0, a_y) != OK) + return NOTOK; + } + + // Return OK to indicate that WalkDown processing may proceed to resolve + // more of the pertinence of this bicomp. + return OK; + } + + // Minor E indicates the desired K4 homeomorph, so we isolate it and return NONEMBEDDABLE + else if (theGraph->IC.minorType & MINORTYPE_E) + { + // Reality check on stack state + if (sp_NonEmpty(theGraph->theStack)) + return NOTOK; + + // Impose consistent orientation on the embedding so we can then + // restore the reduced paths. + if (_OrientVerticesInEmbedding(theGraph) != OK || + _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) + return NOTOK; + + // Set up to isolate minor E + _ClearVisitedFlags(theGraph); + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + + if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) + return NOTOK; + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + + // Isolate the K4 homeomorph + if (_IsolateOuterplanarityObstructionE(theGraph) != OK || + _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) + return NOTOK; + + // Return indication that K4 homeomorph has been found + return NONEMBEDDABLE; + } + + // You never get here in an error-free implementation like this one + return NOTOK; +} + +/**************************************************************************** + _K4_ChooseTypeOfNonOuterplanarityMinor() + This is an overload of the function _ChooseTypeOfNonOuterplanarityMinor() + that avoids processing the whole bicomp rooted by R, e.g. to orient its + vertices or label the vertices of its external face. + This is necessary in particular because of the reduction processing on + MINORTYPE_B. When a K2,3 is found by minor B, we may not be able to find + an entangled K4, so a reduction is performed, but it only eliminates + part of the bicomp and the operations here need to avoid touching parts + of the bicomp that won't be reduced, except by a constant amount of course. + ****************************************************************************/ + +int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R) +{ + int XPrevLink = 1, YPrevLink = 0; + int Wx, WxPrevLink, Wy, WyPrevLink; + + _InitIsolatorContext(theGraph); + + theGraph->IC.v = v; + theGraph->IC.r = R; + + // Reality check on data structure integrity + if (!gp_VirtualVertexInUse(theGraph, R)) + return NOTOK; + + // We are essentially doing a _FindActiveVertices() here, except two things: + // 1) for outerplanarity we know the first vertices along the paths from R + // are the desired vertices because no vertices are "inactive" + // 2) We have purposely not oriented the bicomp, so the XPrevLink result is + // needed to help find the pertinent vertex W + theGraph->IC.x = _GetNeighborOnExtFace(theGraph, R, &XPrevLink); + theGraph->IC.y = _GetNeighborOnExtFace(theGraph, R, &YPrevLink); + + // We are essentially doing a _FindPertinentVertex() here, except two things: + // 1) It is not known whether the reduction of the path through X or the path + // through Y will enable the pertinence of W to be resolved, so it is + // necessary to perform parallel face traversal to find W with a cost no + // more than twice what it will take to resolve the W's pertinence + // (assuming we have to do a reduction rather than finding an entangled K4) + // 2) In the normal _FindPertinentVertex(), the bicomp is already oriented, so + // the "prev link" is hard coded to traverse down the X side. In this + // implementation, the bicomp is purposely not oriented, so we need to know + // XPrevLink and YPrevLink in order to set off in the correct directions. + Wx = theGraph->IC.x; + WxPrevLink = XPrevLink; + Wy = theGraph->IC.y; + WyPrevLink = YPrevLink; + theGraph->IC.w = NIL; + + while (Wx != theGraph->IC.y) + { + Wx = _GetNeighborOnExtFace(theGraph, Wx, &WxPrevLink); + if (PERTINENT(theGraph, Wx)) + { + theGraph->IC.w = Wx; + break; + } + Wy = _GetNeighborOnExtFace(theGraph, Wy, &WyPrevLink); + if (PERTINENT(theGraph, Wy)) + { + theGraph->IC.w = Wy; + break; + } + } + + if (gp_IsNotVertex(theGraph->IC.w)) + return NOTOK; + + // If the root copy is not a root copy of the current vertex v, + // then the Walkdown terminated on a descendant bicomp, which is Minor A. + if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) + theGraph->IC.minorType |= MINORTYPE_A; + + // If W has a pertinent child bicomp, then we've found Minor B. + // Notice this is different from planarity, in which minor B is indicated + // only if the pertinent child bicomp is also future pertinent. + else if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theGraph->IC.w))) + theGraph->IC.minorType |= MINORTYPE_B; + + // The only other result is minor E (we will search for the X-Y path later) + else + theGraph->IC.minorType |= MINORTYPE_E; + + return OK; +} + +/**************************************************************************** + _K4_FindSecondActiveVertexOnLowExtFacePath() + + This method is used in the processing of obstruction A, so it can take + advantage of the bicomp being oriented beforehand. + + This method determines whether there is an active vertex Z other than W on + the path [X, ..., W, ..., Y]. By active, we mean a vertex that connects + by an unembedded edge to either v or an ancestor of v. That is, a vertex + that is pertinent or future pertinent (would be pertinent in a future step + of the embedder). + ****************************************************************************/ + +int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph) +{ + int Z = theGraph->IC.r, ZPrevLink = 1; + + // First we test X for future pertinence only (if it were pertinent, then + // we wouldn't have been blocked up on this bicomp) + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); + if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) + { + theGraph->IC.z = Z; + theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); + return TRUE; + } + + // Now we move on to test all the vertices strictly between X and Y on + // the lower external face path, except W, for either pertinence or + // future pertinence. + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + + while (Z != theGraph->IC.y) + { + if (Z != theGraph->IC.w) + { + gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); + if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) + { + theGraph->IC.z = Z; + theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); + return TRUE; + } + else if (PERTINENT(theGraph, Z)) + { + theGraph->IC.z = Z; + theGraph->IC.uz = theGraph->IC.v; + return TRUE; + } + } + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + // Now we test Y for future pertinence (same explanation as for X above) + gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); + if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) + { + theGraph->IC.z = Z; + theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); + return TRUE; + } + + // We didn't find the desired second vertex, so report FALSE + return FALSE; +} + +/**************************************************************************** + _K4_FindPlanarityActiveVertex() + This service routine starts out at R and heads off in the direction opposite + the prevLink to find the first "planarity active" vertex, i.e. the first one + that is pertinent or future pertinent. + ****************************************************************************/ + +int _K4_FindPlanarityActiveVertex(graphP theGraph, int v, int R, int prevLink, int *pW) +{ + int W = R, WPrevLink = prevLink; + + W = _GetNeighborOnExtFace(theGraph, R, &WPrevLink); + + while (W != R) + { + if (PERTINENT(theGraph, W)) + { + *pW = W; + return OK; + } + else + { + gp_UpdateVertexFuturePertinentChild(theGraph, W, v); + if (FUTUREPERTINENT(theGraph, W, v)) + { + *pW = W; + return OK; + } + } + + W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); + } + + return NOTOK; +} + +/**************************************************************************** + _K4_FindSeparatingInternalEdge() + + Logically, this method is similar to calling MarkHighestXYPath() to + see if there is an internal separator between R and A. + However, that method cannot be called because the bicomp is not oriented. + + Because this is an outerplanarity related algorithm, there are no internal + vertices to contend with, so it is easier to inspect the internal edges + incident to each vertex internal to the path (R ... A), i.e. excluding endpoints, + to see whether any of the edges connects outside of the path [R ... A], + including endpoints. + + In order to avoid adding cost to the core planarity algorithm, we'll just use + the fact that the vertex obstruction types are pre-initialized to the unmarked + state. Until we know if there is a separating internal edge, we cannot be sure + that we can isolate a K4 homeomorph, so we can't afford to initialize the whole + bicomp. The obstruction type of each vertex along the path [R ... A] is marked. + Then, for each vertex in the range (R ... A), if there is any edge that is also + incident to a vertex whose obstruction type is still unmarked, then that edge + is the desired separator edge between R and W (i.e. A), and so we save information + about it. + + If the separator edge is found, then this method sets the *pW to A, and it + sets *pX and *pY values with the endpoints of the separator edge. + The visited flags of the separator edge and its endpoint vertices are not set + at this time because it is easier to set them later as part of the overall K4 + homeomorph isolation. + + Lastly, we restore the unmarked obstruction type settings on the path [R ... A]. + + Returns TRUE if separator edge found or FALSE otherwise + ****************************************************************************/ + +int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY) +{ + int Z, ZPrevLink, e, neighbor; + + // Mark the vertex obstruction type settings along the path [R ... A] + _K4_MarkObstructionTypeOnExternalFacePath(theGraph, R, prevLink, A); + + // Search each of the vertices in the range (R ... A) + *pX = *pY = NIL; + ZPrevLink = prevLink; + Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); + while (Z != A) + { + // Search for a separator among the edges of Z + // It is OK to not bother skipping the external face edges, since we + // know they are marked visited and so are ignored + e = gp_GetFirstArc(theGraph, Z); + while (gp_IsArc(e)) + { + neighbor = gp_GetNeighbor(theGraph, e); + if (gp_GetVertexObstructionType(theGraph, neighbor) == VERTEX_OBSTRUCTIONTYPE_UNMARKED) + { + *pW = A; + *pX = Z; + *pY = neighbor; + break; + } + e = gp_GetNextArc(theGraph, e); + } + + // If we found the separator edge, then we don't need to go on + if (gp_IsVertex(*pX)) + { + break; + } + + // Go to the next vertex + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + // Restore the unmarked obstruction type settings on the path [R ... A] + _K4_UnmarkObstructionTypeOnExternalFacePath(theGraph, R, prevLink, A); + + return gp_IsVertex(*pX) ? TRUE : FALSE; +} + +/**************************************************************************** + _K4_MarkObstructionTypeOnExternalFacePath() + + Assumes A is a vertex along the external face of the bicomp rooted by R. + Marks the obstruction type of vertices along the path (R ... A) that begins + with R's link[1^prevLink] arc. + ****************************************************************************/ + +void _K4_MarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A) +{ + int Z, ZPrevLink; + + gp_SetVertexObstructionType(theGraph, R, VERTEX_OBSTRUCTIONTYPE_MARKED); + ZPrevLink = prevLink; + Z = R; + while (Z != A) + { + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + gp_SetVertexObstructionType(theGraph, Z, VERTEX_OBSTRUCTIONTYPE_MARKED); + } +} + +/**************************************************************************** + _K4_UnmarkObstructionTypeOnExternalFacePath() + + Assumes A is a vertex along the external face of the bicomp rooted by R. + Unmarks the obstruction type of vertices along the path (R ... A) that begins + with R's link[1^prevLink] arc. + ****************************************************************************/ + +void _K4_UnmarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A) +{ + int Z, ZPrevLink; + + gp_ClearVertexObstructionType(theGraph, R); + ZPrevLink = prevLink; + Z = R; + while (Z != A) + { + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + gp_ClearVertexObstructionType(theGraph, Z); + } +} + +/**************************************************************************** + _K4_IsolateMinorA1() + + This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get + a K_4 via the additional path from some vertex Z to the current vertex. + This path may be via some descendant of Z, and it may be a future pertinent + connection to an ancestor of the current vertex. + ****************************************************************************/ + +int _K4_IsolateMinorA1(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (IC->uz < IC->v) + { + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->uz, IC->v) != OK) + return NOTOK; + } + + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->z, IC->dz) != OK) + return NOTOK; + + if (_IsolateOuterplanarityObstructionA(theGraph) != OK) + return NOTOK; + + if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK) + { + return NOTOK; + } + + return OK; +} + +/**************************************************************************** + _K4_IsolateMinorA2() + + This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get + a K_4 via an additional X-Y path within the main bicomp, which is guaranteed + to exist by the time this method is invoked. + One might think to simply invoke _MarkHighestXYPath() to obtain the path, + but the IC->px and IC->py values are already set before invoking this method, + and the bicomp is outerplanar, so the XY path is just an edge. Also, one + subcase of pattern B2 reduces to this pattern, except that the XY path is + determined by the B2 isolator. + ****************************************************************************/ + +int _K4_IsolateMinorA2(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + // We assume the X-Y path was already marked + if (!gp_GetVertexVisited(theGraph, IC->px) || !gp_GetVertexVisited(theGraph, IC->py)) + return NOTOK; + + return _IsolateOuterplanarityObstructionA(theGraph); +} + +/**************************************************************************** + _K4_IsolateMinorB1() + + It is possible to get a K_4 based on the pertinence of w, but we don't do it + that way. If we use the pertinence of w, then we have to eliminate part of + the bicomp external face, which has special cases if a_x==w or a_y==w. + Typically we would mark (r ... a_x ... w ... a_y), which works even when a_y==w, + but if instead a_x==w, then we'd have to mark (w ... a_y ... r). + + Since a_x and a_y are guaranteed to be distinct, it is easier to just ignore + the pertinence of w, and instead use the lower bicomp external face path + as the connection between a_x and a_y. This includes w, but then the + isolation process is the same even if a_x==w or a_y==w. The other two + connections for a_x and a_y are to v and MAX(ux, uy). + ****************************************************************************/ + +int _K4_IsolateMinorB1(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK) + return NOTOK; + + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK) + return NOTOK; + + // The path from the bicomp root to MIN(ux,uy) is marked to ensure the + // connection from the image vertices v and MAX(ux,uy) as well as the + // connection from MAX(ux,uy) through MIN(ux,uy) to (ux==MIN(ux,uy)?x:y) + if (theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK) + return NOTOK; + + // This makes the following connections (a_x ... v), (a_y ... v), and + // (a_x ... w ... a_y), the last being tolerant of a_x==w or a_y==w + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK) + return NOTOK; + + if (_JoinBicomps(theGraph) != OK) + return NOTOK; + + if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK) + return NOTOK; + + if (_AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK) + { + return NOTOK; + } + + return OK; +} + +/**************************************************************************** + _K4_IsolateMinorB2() + + The first subcase of B2 can be reduced to outerplanarity obstruction E + The second subcase of B2 can be reduced to A2 by changing v to u + ****************************************************************************/ + +int _K4_IsolateMinorB2(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + // First subcase, the active vertex is pertinent + if (PERTINENT(theGraph, IC->w)) + { + // We assume the X-Y path was already marked + if (!gp_GetVertexVisited(theGraph, IC->px) || !gp_GetVertexVisited(theGraph, IC->py)) + return NOTOK; + + return _IsolateOuterplanarityObstructionE(theGraph); + } + + // Second subcase, the active vertex is future pertinent + else if (FUTUREPERTINENT(theGraph, IC->w, IC->v)) + { + IC->v = IC->uz; + IC->dw = IC->dz; + + return _K4_IsolateMinorA2(theGraph); + } + + return OK; +} + +/**************************************************************************** + _K4_ReduceBicompToEdge() + + This method is used when reducing the main bicomp of obstruction A to a + single edge (R, W). We first delete all edges from the bicomp except + those on the DFS tree path W to R, then we reduce that DFS tree path to + a DFS tree edge. + + After the reduction, the outerplanarity Walkdown traversal can continue + R to W without being blocked as was the case when R was adjacent to X and Y. + + Returns OK for success, NOTOK for internal (implementation) error. + ****************************************************************************/ + +int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W) +{ + int newEdge; + + if (_OrientVerticesInBicomp(theGraph, R, 0) != OK || + _ClearVisitedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + if (theGraph->functions.fpMarkDFSPath(theGraph, R, W) != OK) + return NOTOK; + if (_K4_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK) + return NOTOK; + + // Now we have to reduce the path W -> R to the DFS tree edge (R, W) + newEdge = _K4_ReducePathToEdge(theGraph, context, EDGE_TYPE_PARENT, + R, gp_GetFirstArc(theGraph, R), W, gp_GetFirstArc(theGraph, W)); + if (gp_IsNotArc(newEdge)) + return NOTOK; + + // Finally, set the visited info state of W to unvisited so that + // the core embedder (esp. Walkup) will not have any problems. + gp_SetVertexVisitedInfo(theGraph, W, theGraph->N); + + return OK; +} + +/**************************************************************************** + _K4_ReducePathComponent() + + This method is invoked when the bicomp rooted by R contains a component + subgraph that is separable from the bicomp by the 2-cut (R, A). The K_4 + homeomorph isolator will have processed a significant fraction of the + component, and so it must be reduced to an edge to ensure that said + processing happens at most once on the component (except for future + operations that are bound to linear time in total by other arguments). + + Because the bicomp is an outerplanar embedding, the component is known to + consists of an external face path plus some internal edges that are parallel + to that path. Otherwise, it wouldn't be separable by the 2-cut (R, A). + + The goal of this method is to reduce the component to the edge (R, A). This + is done in such a way that, if the reduction must be restored, the DFS tree + structure connecting the restored vertices is retained. + + The first step is to ensure that (R, A) is not already just an edge, in which + case no reduction is needed. This can occur if A is future pertinent. + + Assuming a non-trivial reduction component, the next step is to determine + the DFS tree structure within the component. Because it is separable by the + 2-cut (R, A), there are only two cases: + + Case 1: The DFS tree path from A to R is within the reduction component. + + In this case, the DFS tree path is marked, the remaining edges of the + reduction component are eliminated, and then the DFS tree path is reduced to + the the tree edge (R, A). + + Note that the reduction component may also contain descendants of A as well + as vertices that are descendant to R but are neither ancestors nor + descendants of A. This depends on where the tree edge from R meets the + external face path (R ... A). However, the reduction component can only + contribute one path to any future K_4, so it suffices to preserve only the + DFS tree path (A --> R). + + Case 2: The DFS tree path from A to R is not within the reduction component. + + In this case, the external face edge from R leads to a descendant D of A. + We mark that back edge (R, D) plus the DFS tree path (D --> A). The + remaining edges of the reduction component can be removed, and then the + path (R, D, ..., A) is reduced to the edge (R, A). + + For the sake of contradiction, suppose that only part of the DFS tree path + from A to R were contained by the reduction component. Then, a DFS tree edge + would have to exit the reduction component and connect to some vertex not + on the external face path (R, ..., A). This contradicts the assumption that + the reduction subgraph is separable from the bicomp by the 2-cut (R, A). + + Returns OK for success, NOTOK for internal (implementation) error. + ****************************************************************************/ + +int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A) +{ + int e_R, e_A, Z, ZPrevLink, edgeType, invertedFlag = 0; + + // Check whether the external face path (R, ..., A) is just an edge + e_R = gp_GetArc(theGraph, R, 1 ^ prevLink); + if (gp_GetNeighbor(theGraph, e_R) == A) + return OK; + + // Check for Case 1: The DFS tree path from A to R is within the reduction component + if (_K4_TestPathComponentForAncestor(theGraph, R, prevLink, A)) + { + _K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A); + if (theGraph->functions.fpMarkDFSPath(theGraph, R, A) != OK) + return NOTOK; + edgeType = EDGE_TYPE_PARENT; + + invertedFlag = _K4_GetCumulativeOrientationOnDFSPath(theGraph, R, A); + } + + // Otherwise Case 2: The DFS tree path from A to R is not within the reduction component + else + { + _K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A); + Z = gp_GetNeighbor(theGraph, e_R); + gp_SetEdgeVisited(theGraph, e_R); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e_R)); + if (theGraph->functions.fpMarkDFSPath(theGraph, A, Z) != OK) + { + return NOTOK; + } + edgeType = EDGE_TYPE_BACK; + } + + // The path to be kept/reduced is marked, so the other edges can go + if (_K4_DeleteUnmarkedEdgesInPathComponent(theGraph, R, prevLink, A) != OK) + return NOTOK; + + // Clear all the visited flags for safety, except the vertices R and A + // will remain in the embedding, and the core embedder (Walkup) uses a + // value greater than the current vertex to indicate an unvisited vertex + _K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A); + gp_SetVertexVisitedInfo(theGraph, A, theGraph->N); + + // Find the component's remaining edges e_A and e_R incident to A and R + ZPrevLink = prevLink; + Z = R; + while (Z != A) + { + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + e_A = gp_GetArc(theGraph, A, ZPrevLink); + e_R = gp_GetArc(theGraph, R, 1 ^ prevLink); + + // Reduce the path (R ... A) to an edge + e_R = _K4_ReducePathToEdge(theGraph, context, edgeType, R, e_R, A, e_A); + if (gp_IsNotArc(e_R)) + return NOTOK; + + // Preserve the net orientation along the DFS path in the case of a tree edge + if (gp_GetEdgeType(theGraph, e_R) == EDGE_TYPE_CHILD) + { + if (invertedFlag) + gp_SetEdgeFlagInverted(theGraph, e_R); + } + + return OK; +} + +/******************************************************************** + Edge deletion that occurs during a reduction or restoration of a + reduction is augmented by clearing the K_4 search-specific + data members. This is augmentation is not needed in the delete edge + operations that happen once a K_4 homeomorph has been found and + marked for isolation. + ********************************************************************/ + +int _K4_DeleteEdge(graphP theGraph, K4SearchContext *context, int e, int nextLink) +{ + _K4Search_InitEdgeRec(context, e); + _K4Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e)); + + return gp_DeleteEdge(theGraph, e, nextLink); +} + +/******************************************************************** + _K4_DeleteUnmarkedEdgesInBicomp() + + This function deletes from a given biconnected component all edges + whose visited member is zero. + + The stack is used but preserved. In debug mode, NOTOK can result if + there is a stack overflow. This method pushes at most one integer + per vertex in the bicomp. + + This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls + the overloaded _K4_DeleteEdge() rather than gp_DeleteEdge() + + Returns OK on success, NOTOK on implementation failure + ********************************************************************/ + +int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, int BicompRoot) +{ + int V, e; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push(theGraph->theStack, BicompRoot); + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + + e = gp_GetFirstArc(theGraph, V); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); + + e = gp_GetEdgeVisited(theGraph, e) + ? gp_GetNextArc(theGraph, e) + : _K4_DeleteEdge(theGraph, context, e, 0); + } + } + return OK; +} + +/**************************************************************************** + _K4_GetCumulativeOrientationOnDFSPath() + ****************************************************************************/ +int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant) +{ + int e, parent; + int invertedFlag = 0; + + /* If we are marking from a root vertex upward, then go up to the parent + copy before starting the loop */ + + if (gp_IsVirtualVertex(theGraph, descendant)) + descendant = gp_GetPrimaryVertexFromRoot(theGraph, descendant); + + while (descendant != ancestor) + { + if (gp_IsNotVertex(descendant)) + return NOTOK; + + // If we are at a bicomp root, then ascend to its parent copy + if (gp_IsVirtualVertex(theGraph, descendant)) + { + parent = gp_GetPrimaryVertexFromRoot(theGraph, descendant); + } + + // If we are on a regular, non-virtual vertex then get the edge to the parent + else + { + // Scan the edges for the one marked as the DFS parent + parent = NIL; + e = gp_GetFirstArc(theGraph, descendant); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) + { + parent = gp_GetNeighbor(theGraph, e); + break; + } + e = gp_GetNextArc(theGraph, e); + } + + // If the edge to the parent vertex was not found, then the data structure is corrupt + if (gp_IsNotVertex(parent)) + return NOTOK; + + // Add the inversion flag on the child arc to the cumulative result + e = gp_GetTwinArc(theGraph, e); + if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_CHILD || gp_GetNeighbor(theGraph, e) != descendant) + return NOTOK; + invertedFlag ^= gp_GetEdgeFlagInverted(theGraph, e); + } + + // Hop to the parent and reiterate + descendant = parent; + } + + return invertedFlag; +} + +/**************************************************************************** + _K4_TestPathComponentForAncestor() + Tests the external face path between R and A for a DFS ancestor of A. + Returns TRUE if found, FALSE otherwise. + ****************************************************************************/ + +int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A) +{ + int Z, ZPrevLink; + + ZPrevLink = prevLink; + Z = R; + while (Z != A) + { + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + if (Z < A) + return TRUE; + } + return FALSE; +} + +/**************************************************************************** + _K4_ClearVisitedInPathComponent() + + There is a subcomponent of the bicomp rooted by R that is separable by the + 2-cut (R, A). The component contains the external face path from R to A. + The 1^prevLink arc of R is contained in that path (i.e. the first arc if + prevLink indicates the last, or the last arc if prevLink indicates the first). + The prevLink is passed because _GetNeighborOnExtFace() uses the + opposing link to traverse to the "next" vertex. + + All vertices in this desired component are along the external face, so we + traverse along the external face vertices strictly between R and A and + clear all the visited flags of the edges and their incident vertices. + + Note that the vertices along the path (R ... A) only have edges incident + to each other and to R and A because the component is separable by the + (R, A)-cut. + ****************************************************************************/ + +void _K4_ClearVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A) +{ + int Z, ZPrevLink, e; + + ZPrevLink = prevLink; + Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); + while (Z != A) + { + gp_ClearVertexVisited(theGraph, Z); + e = gp_GetFirstArc(theGraph, Z); + while (gp_IsArc(e)) + { + gp_ClearEdgeVisited(theGraph, e); + gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); + + e = gp_GetNextArc(theGraph, e); + } + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } +} + +/**************************************************************************** + _K4_DeleteUnmarkedEdgesInPathComponent() + + There is a subcomponent of the bicomp rooted by R that is separable by the + 2-cut (R, A) and contains the external face path from R to A that includes + the arc gp_GetArc(theGraph, R, 1^prevLink), which is the first arc traversed + by _GetNeighborOnExtFace(..., &prevLink). + + The edges in the component have been marked unvisited except for a path we + intend to preserve. This routine deletes the unvisited edges. + + NOTE: This reduction invalidates the short-circuit extFace data structure, + but it will be repaired for use by WalkUp and WalkDown when the path + component reduction is completed. + + Returns OK on success, NOTOK on internal error + ****************************************************************************/ + +int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A) +{ + int Z, ZPrevLink, e; + K4SearchContext *context = NULL; + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + + if (context == NULL) + { + return NOTOK; + } + + // We need to use the stack to store up the edges we're going to delete. + // We want to make sure there is enough stack capacity to handle it, + // which is of course true because the stack is supposed to be empty. + // We're doing a reduction on a bicomp on which the WalkDown has completed, + // so the stack contains no bicomp roots to merge. + if (sp_NonEmpty(theGraph->theStack)) + return NOTOK; + + // Traverse all vertices internal to the path (R ... A) and push + // all non-visited edges + ZPrevLink = prevLink; + Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); + while (Z != A) + { + e = gp_GetFirstArc(theGraph, Z); + while (gp_IsArc(e)) + { + // The comparison of e to its twin is a useful way of ensuring we + // don't push the edge twice, which is of course only applicable + // when processing an edge whose endpoints are both internal to + // the path (R ... A) + if (!gp_GetEdgeVisited(theGraph, e) && + (e < gp_GetTwinArc(theGraph, e) || + gp_GetNeighbor(theGraph, e) == R || gp_GetNeighbor(theGraph, e) == A)) + { + sp_Push(theGraph->theStack, e); + } + + e = gp_GetNextArc(theGraph, e); + } + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + // Delete all the non-visited edges + while (sp_NonEmpty(theGraph->theStack)) + { + sp_Pop(theGraph->theStack, e); + _K4_DeleteEdge(theGraph, context, e, 0); + } + + return OK; +} + +/**************************************************************************** + _K4_ReducePathToEdge() + + Returns an arc of the edge created on success, a non-arc (NOTOK) on failure + On success, the arc is in the adjacency list of R. The result can be tested + for success or failure using comparison with NIL (non-NIL being success) + ****************************************************************************/ + +int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A) +{ + // Find out the links used in vertex R for edge e_R and in vertex A for edge e_A + int Rlink = gp_GetFirstArc(theGraph, R) == e_R ? 0 : 1; + int Alink = gp_GetFirstArc(theGraph, A) == e_A ? 0 : 1; + + // If the path is more than a single edge, then it must be reduced to an edge. + // Note that even if the path is a single edge, the external face data structure + // must still be modified since many edges connecting the external face have + // been deleted + if (gp_GetNeighbor(theGraph, e_R) != A) + { + int v_R, v_A; + + // Prepare for removing each of the two edges that join the path to the bicomp by + // restoring it if it is a reduction edge (a constant time operation) + if (gp_IsVertex(context->E[e_R].pathConnector)) + { + if (_K4_RestoreReducedPath(theGraph, context, e_R) != OK) + return NOTOK; + + e_R = gp_GetArc(theGraph, R, Rlink); + } + + if (gp_IsVertex(context->E[e_A].pathConnector)) + { + if (_K4_RestoreReducedPath(theGraph, context, e_A) != OK) + return NOTOK; + e_A = gp_GetArc(theGraph, A, Alink); + } + + // Save the vertex neighbors of R and A indicated by e_R and e_A for + // later use in setting up the path connectors. + v_R = gp_GetNeighbor(theGraph, e_R); + v_A = gp_GetNeighbor(theGraph, e_A); + + // Now delete the two edges that join the path to the bicomp. + _K4_DeleteEdge(theGraph, context, e_R, 0); + _K4_DeleteEdge(theGraph, context, e_A, 0); + + // Now add a single edge to represent the path + // We use 1^Rlink, for example, because Rlink was the link from R that indicated e_R, + // so 1^Rlink is the link that indicated e_R in the other arc that was adjacent to e_R. + // We want gp_InsertEdge to place the new arc where e_R was in R's adjacency list + gp_InsertEdge(theGraph, R, gp_GetArc(theGraph, R, Rlink), 1 ^ Rlink, + A, gp_GetArc(theGraph, A, Alink), 1 ^ Alink); + + // Now set up the path connectors so the original path can be recovered if needed. + e_R = gp_GetArc(theGraph, R, Rlink); + context->E[e_R].pathConnector = v_R; + + e_A = gp_GetArc(theGraph, A, Alink); + context->E[e_A].pathConnector = v_A; + + // Also, set the reduction edge's type to preserve the DFS tree structure + gp_SetEdgeType(theGraph, e_R, _ComputeArcType(theGraph, R, A, edgeType)); + gp_SetEdgeType(theGraph, e_A, _ComputeArcType(theGraph, A, R, edgeType)); + } + + // Set the external face data structure + gp_SetExtFaceVertex(theGraph, R, Rlink, A); + gp_SetExtFaceVertex(theGraph, A, Alink, R); + + // If the edge represents an entire bicomp, then more external face + // settings are needed. + if (gp_GetFirstArc(theGraph, R) == gp_GetLastArc(theGraph, R)) + { + gp_SetExtFaceVertex(theGraph, R, 1 ^ Rlink, A); + gp_SetExtFaceVertex(theGraph, A, 1 ^ Alink, R); + } + + return e_R; +} + +/**************************************************************************** + _K4_RestoreReducedPath() + + Given an edge record of an edge used to reduce a path, we want to restore + the path in constant time. + The path may contain more reduction edges internally, but we do not + search for and process those since it would violate the constant time + bound required of this function. + + Note that we don't bother amending the external face data structure because + a reduced path is only restored when it will shortly be reduced again or + when we don't really need the external face data structure anymore. + + Return OK on success, NOTOK on failure + ****************************************************************************/ + +int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e) +{ + int eTwin, u, v, w, x; + int e0, e1, eTwin0, eTwin1; + + if (gp_IsNotVertex(context->E[e].pathConnector)) + return OK; + + eTwin = gp_GetTwinArc(theGraph, e); + + u = gp_GetNeighbor(theGraph, eTwin); + v = context->E[e].pathConnector; + w = context->E[eTwin].pathConnector; + x = gp_GetNeighbor(theGraph, e); + + // Get the locations of the EdgeRecs between which the new EdgeRecs + // must be added in order to reconnect the path parallel to the edge. + e0 = gp_GetNextArc(theGraph, e); + e1 = gp_GetPrevArc(theGraph, e); + eTwin0 = gp_GetNextArc(theGraph, eTwin); + eTwin1 = gp_GetPrevArc(theGraph, eTwin); + + // We first delete the edge represented by e and eTwin. We do so before + // restoring the path to ensure we do not exceed the maximum arc capacity. + _K4_DeleteEdge(theGraph, context, e, 0); + + // Now we add the two edges to reconnect the reduced path represented + // by the edge [e, eTwin]. The edge record in u is added between e0 and e1. + // Likewise, the new edge record in x is added between eTwin0 and eTwin1. + if (gp_IsArc(e0)) + { + if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) + return NOTOK; + } + else + { + if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) + return NOTOK; + } + + if (gp_IsArc(eTwin0)) + { + if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) + return NOTOK; + } + else + { + if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) + return NOTOK; + } + + // Set the types of the newly added edges. In both cases, the first of the two + // vertex parameters is known to be degree 2 because they are internal to the + // path being restored, so this operation is constant time. + if (_SetEdgeType(theGraph, v, u) != OK || _SetEdgeType(theGraph, w, x) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _K4_RestoreAndOrientReducedPaths() + + This function searches the embedding for any edges that are specially marked + as being representative of a path that was previously reduced to a single edge + by _ReducePathToEdge(). This method restores the path by replacing the edge + with the path. + + Note that the new path may contain more reduction edges, and these will be + iteratively expanded by the outer for loop. Equally, a reduced path may + be restored into a path that itself is a reduction path that will only be + attached to the embedding by some future step of the outer loop. + + The vertices along the path being restored must be given a consistent + orientation with the endpoints. It is expected that the embedding + will have been oriented prior to this operation. + ****************************************************************************/ + +int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context) +{ + int EsizeOccupied, e, eTwin, u, v, w, x, visited; + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;) + { + if (gp_IsVertex(context->E[e].pathConnector)) + { + visited = gp_GetEdgeVisited(theGraph, e); + + eTwin = gp_GetTwinArc(theGraph, e); + u = gp_GetNeighbor(theGraph, eTwin); + v = context->E[e].pathConnector; + w = context->E[eTwin].pathConnector; + x = gp_GetNeighbor(theGraph, e); + + if (_K4_RestoreReducedPath(theGraph, context, e) != OK) + return NOTOK; + + // If the path is on the external face, orient it + if (gp_GetNeighbor(theGraph, gp_GetFirstArc(theGraph, u)) == v || + gp_GetNeighbor(theGraph, gp_GetLastArc(theGraph, u)) == v) + { + // Reality check: ensure the path is connected to the + // external face at both vertices. + if (gp_GetNeighbor(theGraph, gp_GetFirstArc(theGraph, x)) != w && + gp_GetNeighbor(theGraph, gp_GetLastArc(theGraph, x)) != w) + return NOTOK; + + if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK) + return NOTOK; + } + + if (visited) + { + if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) + return NOTOK; + } + else + { + if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) + return NOTOK; + } + } + else + e += 2; + } + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.h new file mode 100644 index 000000000..02f7409a8 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_K4SEARCH_H +#define GRAPH_K4SEARCH_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graphStructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define K4SEARCH_NAME "K4Search" + + int gp_AttachK4Search(graphP theGraph); + int gp_DetachK4Search(graphP theGraph); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.private.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.private.h new file mode 100644 index 000000000..73c1de898 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.private.h @@ -0,0 +1,61 @@ +#ifndef GRAPH_K4SEARCH_PRIVATE_H +#define GRAPH_K4SEARCH_PRIVATE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graph.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Additional equipment for each EdgeRec + + pathConnector: + Used in the edge records (arcs) of a reduction edge to indicate the + endpoints of a path that has been reduced from (removed from) the + embedding so that the search for a K4 can continue. + We only need a pathConnector because we reduce subgraphs that are + separable by a 2-cut, so they can contribute at most one path to a + subgraph homeomorphic to K4, if one is indeed found. Thus, we first + delete all edges except for the desired path(s), then we reduce any + retained path to an edge. + */ + typedef struct + { + int pathConnector; + } K4Search_EdgeRec; + + typedef K4Search_EdgeRec *K4Search_EdgeRecP; + + /* Additional equipment for each vertex: None */ + + typedef struct + { + // Helps distinguish initialize from re-initialize + int initialized; + + // The graph that this context augments + graphP theGraph; + + // Parallel array for additional edge level equipment + K4Search_EdgeRecP E; + + // Overloaded function pointers + graphFunctionTable functions; + + // Internal variable for converting a tail recursion into a simple loop + int handlingBlockedBicomp; + + } K4SearchContext; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c new file mode 100644 index 000000000..ab4bb5815 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search_Extensions.c @@ -0,0 +1,495 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "graphK4Search.private.h" +#include "graphK4Search.h" + +extern int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R); + +extern int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, + int *degrees, int *imageVerts); + +extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, + int *imageVerts, int maxNumImageVerts); + +extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); + +/* Forward declarations of local functions */ + +void _K4Search_ClearStructures(K4SearchContext *context); +int _K4Search_CreateStructures(K4SearchContext *context); +int _K4Search_InitStructures(K4SearchContext *context); + +void _K4Search_InitEdgeRec(K4SearchContext *context, int e); + +/* Forward declarations of overloading functions */ +int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); +int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); +int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); +int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); + +int _K4Search_InitGraph(graphP theGraph, int N); +void _K4Search_ReinitializeGraph(graphP theGraph); +int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); + +/* Forward declarations of functions used by the extension system */ + +void *_K4Search_DupContext(void *pContext, void *theGraph); +void _K4Search_FreeContext(void *); + +/**************************************************************************** + * K4SEARCH_ID - the variable used to hold the integer identifier for this + * extension, enabling this feature's extension context to be distinguished + * from other features' extension contexts that may be attached to a graph. + ****************************************************************************/ + +int K4SEARCH_ID = 0; + +/**************************************************************************** + gp_AttachK4Search() + + This function adjusts the graph data structure to attach the K4 search + feature. + ****************************************************************************/ + +int gp_AttachK4Search(graphP theGraph) +{ + K4SearchContext *context = NULL; + + // If the K4 search feature has already been attached to the graph, + // then there is no need to attach it again + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + if (context != NULL) + { + return OK; + } + + // Allocate a new extension context + context = (K4SearchContext *)malloc(sizeof(K4SearchContext)); + if (context == NULL) + { + return NOTOK; + } + + // First, tell the context that it is not initialized + context->initialized = 0; + + // Save a pointer to theGraph in the context + context->theGraph = theGraph; + + // Put the overload functions into the context function table. + // gp_AddExtension will overload the graph's functions with these, and + // return the base function pointers in the context function table + memset(&context->functions, 0, sizeof(graphFunctionTable)); + context->functions.fpHandleBlockedBicomp = _K4Search_HandleBlockedBicomp; + context->functions.fpEmbedPostprocess = _K4Search_EmbedPostprocess; + context->functions.fpCheckEmbeddingIntegrity = _K4Search_CheckEmbeddingIntegrity; + context->functions.fpCheckObstructionIntegrity = _K4Search_CheckObstructionIntegrity; + + context->functions.fpInitGraph = _K4Search_InitGraph; + context->functions.fpReinitializeGraph = _K4Search_ReinitializeGraph; + context->functions.fpEnsureArcCapacity = _K4Search_EnsureArcCapacity; + + _K4Search_ClearStructures(context); + + // Store the K4 search context, including the data structure and the + // function pointers, as an extension of the graph + if (gp_AddExtension(theGraph, &K4SEARCH_ID, (void *)context, + _K4Search_DupContext, _K4Search_FreeContext, + &context->functions) != OK) + { + _K4Search_FreeContext(context); + return NOTOK; + } + + // Create the K4-specific structures if the size of the graph is known + // Attach functions are always invoked after gp_New(), but if a graph + // extension must be attached before gp_Read(), then the attachment + // also happens before gp_InitGraph(), which means N==0. + // However, sometimes a feature is attached after gp_InitGraph(), in + // which case N > 0 + if (theGraph->N > 0) + { + if (_K4Search_CreateStructures(context) != OK || + _K4Search_InitStructures(context) != OK) + { + _K4Search_FreeContext(context); + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + gp_DetachK4Search() + ********************************************************************/ + +int gp_DetachK4Search(graphP theGraph) +{ + return gp_RemoveExtension(theGraph, K4SEARCH_ID); +} + +/******************************************************************** + _K4Search_ClearStructures() + ********************************************************************/ + +void _K4Search_ClearStructures(K4SearchContext *context) +{ + if (!context->initialized) + { + // Before initialization, the pointers are stray, not NULL + // Once NULL or allocated, free() or LCFree() can do the job + context->E = NULL; + + context->handlingBlockedBicomp = FALSE; + + context->initialized = 1; + } + else + { + if (context->E != NULL) + { + free(context->E); + context->E = NULL; + } + context->handlingBlockedBicomp = FALSE; + } +} + +/******************************************************************** + _K4Search_CreateStructures() + Create uninitialized structures for the vertex and edge levels, and + initialized structures for the graph level + ********************************************************************/ +int _K4Search_CreateStructures(K4SearchContext *context) +{ + int Esize = gp_EdgeIndexBound(context->theGraph); + + if (context->theGraph->N <= 0) + return NOTOK; + + if ((context->E = (K4Search_EdgeRecP)malloc(Esize * sizeof(K4Search_EdgeRec))) == NULL || + 0) + { + return NOTOK; + } + + return OK; +} + +/******************************************************************** + _K4Search_InitStructures() + ********************************************************************/ +int _K4Search_InitStructures(K4SearchContext *context) +{ + memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K4Search_EdgeRec)); + // N.B. This is the legacy API-based approach to initializing the structures + // required for the K_4 search graph algorithm extension. + // int e, Esize; + + // Esize = gp_EdgeIndexBound(context->theGraph); + // for (e = gp_GetFirstEdge(context->theGraph); e < Esize; e++) + // _K4Search_InitEdgeRec(context, e); + + return OK; +} + +/******************************************************************** + ********************************************************************/ + +int _K4Search_InitGraph(graphP theGraph, int N) +{ + K4SearchContext *context = NULL; + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + + if (context == NULL) + return NOTOK; + + theGraph->N = N; + theGraph->NV = N; + if (theGraph->arcCapacity == 0) + theGraph->arcCapacity = 2 * DEFAULT_EDGE_LIMIT * N; + + if (_K4Search_CreateStructures(context) != OK || + _K4Search_InitStructures(context) != OK) + return NOTOK; + + context->functions.fpInitGraph(theGraph, N); + + return OK; +} + +/******************************************************************** + ********************************************************************/ + +void _K4Search_ReinitializeGraph(graphP theGraph) +{ + K4SearchContext *context = NULL; + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + + if (context != NULL) + { + // Reinitialize the graph + context->functions.fpReinitializeGraph(theGraph); + + // Do the reinitialization that is specific to this module + _K4Search_InitStructures(context); + } +} + +/******************************************************************** + The current implementation does not support an increase of arc + (edge record) capacity once the extension is attached to the graph + data structure. This is only due to not being necessary to support. + For now, it is easy to ensure the correct capacity before attaching + the extension, but support could be added later if there is some + reason to do so. + ********************************************************************/ + +int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) +{ + return NOTOK; +} + +/******************************************************************** + _K4Search_DupContext() + ********************************************************************/ + +void *_K4Search_DupContext(void *pContext, void *theGraph) +{ + K4SearchContext *context = (K4SearchContext *)pContext; + K4SearchContext *newContext = (K4SearchContext *)malloc(sizeof(K4SearchContext)); + + if (newContext != NULL) + { + int Esize = gp_EdgeIndexBound((graphP)theGraph); + + *newContext = *context; + + newContext->theGraph = (graphP)theGraph; + + newContext->initialized = 0; + _K4Search_ClearStructures(newContext); + if (((graphP)theGraph)->N > 0) + { + if (_K4Search_CreateStructures(newContext) != OK) + { + _K4Search_FreeContext(newContext); + return NULL; + } + + memcpy(newContext->E, context->E, Esize * sizeof(K4Search_EdgeRec)); + } + } + + return newContext; +} + +/******************************************************************** + _K4Search_FreeContext() + ********************************************************************/ + +void _K4Search_FreeContext(void *pContext) +{ + K4SearchContext *context = (K4SearchContext *)pContext; + + _K4Search_ClearStructures(context); + free(pContext); +} + +/******************************************************************** + ********************************************************************/ + +void _K4Search_InitEdgeRec(K4SearchContext *context, int e) +{ + context->E[e].pathConnector = NIL; +} + +/******************************************************************** + _K4Search_HandleBlockedBicomp() + Returns OK if no K4 homeomorph found and blockage cleared (OK to + proceed with Walkdown embedding) + NONEMBEDDABLE if K4 homeomorph found, and Walkdown embedding + should be terminated. + NOTOK on internal error + ********************************************************************/ + +int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) +{ + K4SearchContext *context = NULL; + + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + if (context == NULL) + return NOTOK; + + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) + { + int RetVal = OK; + + // If invoked on a descendant bicomp, then we push its root then search once + // since not finding a K4 homeomorph will also clear the blockage and allow + // the Walkdown to continue walking down + if (R != RootVertex) + { + sp_Push2(theGraph->theStack, R, 0); + if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, R)) == OK) + { + // If the Walkdown will be told it is OK to continue, then we have to take the descendant + // bicomp root back off the stack so the Walkdown can try to descend to it again. + // The top of stack has the pair R and 0/1 direction Walkdown traversal proceeds from R + // Need only R, so pop and discard the direction, then pop R + sp_Pop2_Discard1(theGraph->theStack, R); + + // And we have to clear the indicator of the minor A that was reduced, since it was eliminated. + theGraph->IC.minorType = 0; + } + } + + // Otherwise, if invoked on a child bicomp rooted by a virtual copy of v, + // then we search for a K4 homeomorph, and if OK is returned, then that indicates + // the blockage has been cleared and it is OK to Walkdown the bicomp. + // But the Walkdown finished, already, so we launch it again. + // If the Walkdown returns OK then all forward arcs were embedded. If NONEMBEDDABLE + // is returned, then the bicomp got blocked again, so we have to reiterate the K4 search + else + { + // If Walkdown has recursively called this handler on the bicomp rooted by RootVertex, + // then it is still blocked, so we just return NONEMBEDDABLE, which causes Walkdown to + // return to the loop below and signal that the loop should invoke the Walkdown again. + if (context->handlingBlockedBicomp) + return NONEMBEDDABLE; + + context->handlingBlockedBicomp = TRUE; + do + { + // Detect whether bicomp can be used to find a K4 homeomorph. It it does, then + // it returns NONEMBEDDABLE so we break the search because we found the desired K4 + // If OK is returned, then the blockage was cleared and it is OK to Walkdown again. + if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, RootVertex)) != OK) + break; + + // Walkdown again to embed more edges. If Walkdown returns OK, then all remaining + // edges to its descendants are embedded, so we'll get out of this loop. If Walkdown + // detects that it still has not embedded all the edges to descendants of the bicomp's + // root edge child, then Walkdown calls this routine again, and the above non-reentrancy + // code returns NONEMBEDDABLE, causing this loop to search again for a K4. + theGraph->IC.minorType = 0; + RetVal = theGraph->functions.fpWalkDown(theGraph, v, RootVertex); + + // Except if the Walkdown returns NONEMBEDDABLE due to finding a K4 homeomorph entangled + // with a descendant bicomp (the R != RootVertex case above), then it was found + // entangled with Minor A, so we can stop the search if minor A is detected + if (theGraph->IC.minorType & MINORTYPE_A) + break; + + } while (RetVal == NONEMBEDDABLE); + context->handlingBlockedBicomp = FALSE; + } + + return RetVal; + } + else + { + return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + // For K4 search, we just return the edge embedding result because the + // search result has been obtained already. + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) + { + return edgeEmbeddingResult; + } + + // When not searching for K4, we let the superclass do the work + else + { + K4SearchContext *context = NULL; + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) +{ + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) + { + return OK; + } + + // When not searching for K4, we let the superclass do the work + else + { + K4SearchContext *context = NULL; + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); + } + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) +{ + // When searching for K4, we ensure that theGraph is a subgraph of + // the original graph and that it contains a K4 homeomorph + if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) + { + int degrees[4], imageVerts[4]; + + if (_TestSubgraph(theGraph, origGraph) != TRUE) + return NOTOK; + + if (_getImageVertices(theGraph, degrees, 3, imageVerts, 4) != OK) + return NOTOK; + + if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE) + { + return OK; + } + + return NOTOK; + } + + // When not searching for K4, we let the superclass do the work + else + { + K4SearchContext *context = NULL; + gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); + + if (context != NULL) + { + return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); + } + } + + return NOTOK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.c new file mode 100644 index 000000000..39195e110 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.c @@ -0,0 +1,41 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "g6-api-utilities.h" + +int _getMaxEdgeCount(int graphOrder) +{ + return (graphOrder * (graphOrder - 1)) / 2; +} + +int _getNumCharsForGraphEncoding(int graphOrder) +{ + int maxNumEdges = _getMaxEdgeCount(graphOrder); + + return (maxNumEdges / 6) + (maxNumEdges % 6 ? 1 : 0); +} + +int _getNumCharsForGraphOrder(int graphOrder) +{ + if (graphOrder > 0 && graphOrder < 63) + { + return 1; + } + else if (graphOrder >= 63 && graphOrder <= 100000) + { + return 4; + } + + return -1; +} + +int _getExpectedNumPaddingZeroes(const int graphOrder, const int numChars) +{ + int maxNumEdges = _getMaxEdgeCount(graphOrder); + int expectedNumPaddingZeroes = numChars * 6 - maxNumEdges; + + return expectedNumPaddingZeroes; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.h new file mode 100644 index 000000000..15239d9ff --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-api-utilities.h @@ -0,0 +1,24 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef G6_API_UTILITIES +#define G6_API_UTILITIES + +#ifdef __cplusplus +extern "C" +{ +#endif + + int _getMaxEdgeCount(int); + int _getNumCharsForGraphEncoding(int); + int _getNumCharsForGraphOrder(int); + int _getExpectedNumPaddingZeroes(const int, const int); + +#ifdef __cplusplus +} +#endif + +#endif /* G6_API_UTILITIES */ diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c new file mode 100644 index 000000000..e97fd44f9 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c @@ -0,0 +1,728 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include + +#include "g6-read-iterator.h" +#include "g6-api-utilities.h" + +int allocateG6ReadIterator(G6ReadIterator **ppG6ReadIterator, graphP pGraph) +{ + int exitCode = OK; + + if (ppG6ReadIterator != NULL && (*ppG6ReadIterator) != NULL) + { + ErrorMessage("G6ReadIterator is not NULL and therefore can't be allocated.\n"); + return NOTOK; + } + + // numGraphsRead, graphOrder, numCharsForGraphOrder, + // numCharsForGraphEncoding, and currGraphBuffSize all set to 0 + (*ppG6ReadIterator) = (G6ReadIterator *)calloc(1, sizeof(G6ReadIterator)); + + if ((*ppG6ReadIterator) == NULL) + { + ErrorMessage("Unable to allocate memory for G6ReadIterator.\n"); + return NOTOK; + } + + (*ppG6ReadIterator)->g6Input = NULL; + + if (pGraph == NULL) + { + ErrorMessage("Must allocate graph to be used by G6ReadIterator.\n"); + + exitCode = freeG6ReadIterator(ppG6ReadIterator); + + if (exitCode != OK) + ErrorMessage("Unable to free the G6ReadIterator.\n"); + } + else + { + (*ppG6ReadIterator)->currGraph = pGraph; + } + + return exitCode; +} + +bool _isG6ReadIteratorAllocated(G6ReadIterator *pG6ReadIterator) +{ + bool g6ReadIteratorIsAllocated = true; + + if (pG6ReadIterator == NULL || pG6ReadIterator->currGraph == NULL) + { + g6ReadIteratorIsAllocated = false; + } + + return g6ReadIteratorIsAllocated; +} + +int getNumGraphsRead(G6ReadIterator *pG6ReadIterator, int *pNumGraphsRead) +{ + if (pG6ReadIterator == NULL) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + (*pNumGraphsRead) = pG6ReadIterator->numGraphsRead; + + return OK; +} + +int getOrderOfGraphToRead(G6ReadIterator *pG6ReadIterator, int *pGraphOrder) +{ + if (pG6ReadIterator == NULL) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + (*pGraphOrder) = pG6ReadIterator->graphOrder; + + return OK; +} + +int getPointerToGraphReadIn(G6ReadIterator *pG6ReadIterator, graphP *ppGraph) +{ + if (pG6ReadIterator == NULL) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + (*ppGraph) = pG6ReadIterator->currGraph; + + return OK; +} + +int beginG6ReadIterationFromG6StrOrFile(G6ReadIterator *pG6ReadIterator, strOrFileP g6InputContainer) +{ + if ( + sf_ValidateStrOrFile(g6InputContainer) != OK || + (g6InputContainer->theStr != NULL && sb_GetSize(g6InputContainer->theStr) == 0)) + { + ErrorMessage("Invalid g6InputContainer; must contain either valid input stream or non-empty string.\n"); + return NOTOK; + } + + pG6ReadIterator->g6Input = g6InputContainer; + + return _beginG6ReadIteration(pG6ReadIterator); +} + +int _beginG6ReadIteration(G6ReadIterator *pG6ReadIterator) +{ + int exitCode = OK; + char charConfirmation = EOF; + + char messageContents[MAXLINE + 1]; + + strOrFileP g6Input = pG6ReadIterator->g6Input; + + int firstChar = sf_getc(g6Input); + if (firstChar == EOF) + { + ErrorMessage(".g6 infile is empty.\n"); + return NOTOK; + } + else + { + charConfirmation = sf_ungetc((char)firstChar, g6Input); + + if (charConfirmation != firstChar) + { + ErrorMessage("Unable to ungetc first character.\n"); + return NOTOK; + } + + if (firstChar == '>') + { + exitCode = _processAndCheckHeader(g6Input); + if (exitCode != OK) + { + ErrorMessage("Unable to process and check .g6 infile header.\n"); + return exitCode; + } + } + } + + int lineNum = 1; + firstChar = sf_getc(g6Input); + charConfirmation = sf_ungetc((char)firstChar, g6Input); + + if (charConfirmation != firstChar) + { + ErrorMessage("Unable to ungetc first character.\n"); + return NOTOK; + } + + if (!_firstCharIsValid((char)firstChar, lineNum)) + return NOTOK; + + // Despite the general specification indicating that n \in [0, 68,719,476,735], + // in practice n will be limited such that an integer will suffice in storing it. + int graphOrder = -1; + exitCode = _getGraphOrder(g6Input, &graphOrder); + + if (exitCode != OK) + { + sprintf(messageContents, "Invalid graph order on line %d of .g6 file.\n", lineNum); + ErrorMessage(messageContents); + return exitCode; + } + + if (pG6ReadIterator->currGraph->N == 0) + { + exitCode = gp_InitGraph(pG6ReadIterator->currGraph, graphOrder); + + if (exitCode != OK) + { + sprintf(messageContents, "Unable to initialize graph datastructure with order %d for graph on line %d of the .g6 file.\n", graphOrder, lineNum); + ErrorMessage(messageContents); + return exitCode; + } + + pG6ReadIterator->graphOrder = graphOrder; + } + else + { + if (pG6ReadIterator->currGraph->N != graphOrder) + { + sprintf(messageContents, "Graph datastructure passed to G6ReadIterator already initialized with graph order %d,\n", pG6ReadIterator->currGraph->N); + ErrorMessage(messageContents); + sprintf(messageContents, "\twhich doesn't match the graph order %d specified in the file.\n", graphOrder); + ErrorMessage(messageContents); + return NOTOK; + } + else + { + gp_ReinitializeGraph(pG6ReadIterator->currGraph); + pG6ReadIterator->graphOrder = graphOrder; + } + } + + // Ensures zero-based flag is set regardless of whether the graph was initialized or reinitialized. + pG6ReadIterator->currGraph->internalFlags |= FLAGS_ZEROBASEDIO; + + pG6ReadIterator->numCharsForGraphOrder = _getNumCharsForGraphOrder(graphOrder); + pG6ReadIterator->numCharsForGraphEncoding = _getNumCharsForGraphEncoding(graphOrder); + // Must add 3 bytes for newline, possible carriage return, and null terminator + pG6ReadIterator->currGraphBuffSize = pG6ReadIterator->numCharsForGraphOrder + pG6ReadIterator->numCharsForGraphEncoding + 3; + pG6ReadIterator->currGraphBuff = (char *)calloc(pG6ReadIterator->currGraphBuffSize, sizeof(char)); + + if (pG6ReadIterator->currGraphBuff == NULL) + { + ErrorMessage("Unable to allocate memory for currGraphBuff.\n"); + exitCode = NOTOK; + } + + return exitCode; +} + +int _processAndCheckHeader(strOrFileP g6Input) +{ + int exitCode = OK; + + if (g6Input == NULL) + { + ErrorMessage("Invalid .g6 string-or-file container.\n"); + return NOTOK; + } + + char *correctG6Header = ">>graph6<<"; + char *sparse6Header = ">>sparse6<"; + char *digraph6Header = ">>digraph6"; + + char headerCandidateChars[11]; + headerCandidateChars[0] = '\0'; + + for (int i = 0; i < 10; i++) + { + headerCandidateChars[i] = sf_getc(g6Input); + } + + headerCandidateChars[10] = '\0'; + + if (strcmp(correctG6Header, headerCandidateChars) != 0) + { + if (strcmp(sparse6Header, headerCandidateChars) == 0) + ErrorMessage("Graph file is sparse6 format, which is not supported.\n"); + else if (strcmp(digraph6Header, headerCandidateChars) == 0) + ErrorMessage("Graph file is digraph6 format, which is not supported.\n"); + else + ErrorMessage("Invalid header for .g6 file.\n"); + + exitCode = NOTOK; + } + + return exitCode; +} + +bool _firstCharIsValid(char c, const int lineNum) +{ + bool isValidFirstChar = false; + + if (strchr(":;&", c) != NULL) + { + char messageContents[MAXLINE + 1]; + sprintf(messageContents, "Invalid first character on line %d, i.e. one of ':', ';', or '&'; aborting.\n", lineNum); + ErrorMessage(messageContents); + } + else + isValidFirstChar = true; + + return isValidFirstChar; +} + +int _getGraphOrder(strOrFileP g6Input, int *graphOrder) +{ + int exitCode = OK; + + if (g6Input == NULL) + { + ErrorMessage("Invalid string-or-file container for .g6 input.\n"); + return NOTOK; + } + + // Since geng: n must be in the range 1..32, and since edge-addition-planarity-suite + // processing of random graphs may only handle up to n = 100,000, we will only check + // if 1 or 4 bytes are necessary + int n = 0; + int graphChar = sf_getc(g6Input); + if (graphChar == 126) + { + if ((graphChar = sf_getc(g6Input)) == 126) + { + ErrorMessage("Graph order is too large; format suggests that 258048 <= n <= 68719476735, but we only support n <= 100000.\n"); + return NOTOK; + } + + sf_ungetc((char)graphChar, g6Input); + + for (int i = 2; i >= 0; i--) + { + graphChar = sf_getc(g6Input) - 63; + n |= graphChar << (6 * i); + } + + if (n > 100000) + { + ErrorMessage("Graph order is too large; we only support n <= 100000.\n"); + return NOTOK; + } + } + else if (graphChar > 62 && graphChar < 126) + n = graphChar - 63; + else + { + ErrorMessage("Graph order is too small; character doesn't correspond to a printable ASCII character.\n"); + return NOTOK; + } + + (*graphOrder) = n; + + return exitCode; +} + +int readGraphUsingG6ReadIterator(G6ReadIterator *pG6ReadIterator) +{ + int exitCode = OK; + + char messageContents[MAXLINE + 1]; + + if (!_isG6ReadIteratorAllocated(pG6ReadIterator)) + { + ErrorMessage("G6ReadIterator is not allocated.\n"); + return NOTOK; + } + + strOrFileP g6Input = pG6ReadIterator->g6Input; + + if (g6Input == NULL) + { + ErrorMessage("Pointer to .g6 string-or-file container is NULL.\n"); + return NOTOK; + } + + int numGraphsRead = pG6ReadIterator->numGraphsRead; + + char *currGraphBuff = pG6ReadIterator->currGraphBuff; + + if (currGraphBuff == NULL) + { + ErrorMessage("currGraphBuff string is null.\n"); + return NOTOK; + } + + const int graphOrder = pG6ReadIterator->graphOrder; + const int numCharsForGraphOrder = pG6ReadIterator->numCharsForGraphOrder; + const int numCharsForGraphEncoding = pG6ReadIterator->numCharsForGraphEncoding; + const int currGraphBuffSize = pG6ReadIterator->currGraphBuffSize; + + graphP currGraph = pG6ReadIterator->currGraph; + + char firstChar = '\0'; + char *graphEncodingChars = NULL; + if (sf_fgets(currGraphBuff, currGraphBuffSize, g6Input) != NULL) + { + numGraphsRead++; + firstChar = currGraphBuff[0]; + + if (!_firstCharIsValid(firstChar, numGraphsRead)) + return NOTOK; + + // From https://stackoverflow.com/a/28462221, strcspn finds the index of the first + // char in charset; this way, I replace the char at that index with the null-terminator + currGraphBuff[strcspn(currGraphBuff, "\n\r")] = '\0'; // works for LF, CR, CRLF, LFCR, ... + + // If the line was too long, then we would have placed the null terminator at the final + // index (where it already was; see strcpn docs), and the length of the string will be + // longer than the line should have been, i.e. orderOffset + numCharsForGraphRepr + if ((int)strlen(currGraphBuff) != (((numGraphsRead == 1) ? 0 : numCharsForGraphOrder) + numCharsForGraphEncoding)) + { + sprintf(messageContents, "Invalid line length read on line %d\n", numGraphsRead); + ErrorMessage(messageContents); + return NOTOK; + } + + if (numGraphsRead > 1) + { + exitCode = _checkGraphOrder(currGraphBuff, graphOrder); + + if (exitCode != OK) + { + sprintf(messageContents, "Order of graph on line %d is incorrect.\n", numGraphsRead); + ErrorMessage(messageContents); + return exitCode; + } + } + + // On first line, we have already processed the characters corresponding to the graph + // order, so there's no need to apply the offset. On subsequent lines, the orderOffset + // must be applied so that we are only starting validation on the byte corresponding to + // the encoding of the adjacency matrix. + graphEncodingChars = (numGraphsRead == 1) ? currGraphBuff : currGraphBuff + numCharsForGraphOrder; + + exitCode = _validateGraphEncoding(graphEncodingChars, graphOrder, numCharsForGraphEncoding); + + if (exitCode != OK) + { + sprintf(messageContents, "Graph on line %d is invalid.", numGraphsRead); + ErrorMessage(messageContents); + return exitCode; + } + + if (numGraphsRead > 1) + { + gp_ReinitializeGraph(currGraph); + // Ensures zero-based flag is set after reinitializing graph. + currGraph->internalFlags |= FLAGS_ZEROBASEDIO; + } + + exitCode = _decodeGraph(graphEncodingChars, graphOrder, numCharsForGraphEncoding, currGraph); + + if (exitCode != OK) + { + sprintf(messageContents, "Unable to interpret bits on line %d to populate adjacency matrix.\n", numGraphsRead); + ErrorMessage(messageContents); + return exitCode; + } + + pG6ReadIterator->numGraphsRead = numGraphsRead; + } + else + pG6ReadIterator->currGraph = NULL; + + return exitCode; +} + +int _checkGraphOrder(char *graphBuff, int graphOrder) +{ + int exitCode = OK; + + int n = 0; + char currChar = graphBuff[0]; + if (currChar == 126) + { + if (graphBuff[1] == 126) + { + ErrorMessage("Can only handle graphs of order <= 100,000.\n"); + return NOTOK; + } + else if (graphBuff[1] > 126) + { + ErrorMessage("Invalid graph order signifier.\n"); + return NOTOK; + } + + int orderCharIndex = 2; + + for (int i = 1; i < 4; i++) + n |= (graphBuff[i] - 63) << (6 * orderCharIndex--); + } + else if (currChar > 62 && currChar < 126) + n = currChar - 63; + else + { + ErrorMessage("Character doesn't correspond to a printable ASCII character.\n"); + return NOTOK; + } + + if (n != graphOrder) + { + char messageContents[MAXLINE + 1]; + sprintf(messageContents, "Graph order %d doesn't match expected graph order %d", n, graphOrder); + ErrorMessage(messageContents); + exitCode = NOTOK; + } + + return exitCode; +} + +int _validateGraphEncoding(char *graphBuff, const int graphOrder, const int numChars) +{ + int exitCode = OK; + + char messageContents[MAXLINE + 1]; + + // Num edges of the graph (and therefore the number of bits) is (n * (n-1))/2, and + // since each resulting byte needs to correspond to an ascii character between 63 and 126, + // each group is only comprised of 6 bits (to which we add 63 for the final byte value) + int expectedNumChars = _getNumCharsForGraphEncoding(graphOrder); + int numCharsForGraphEncoding = strlen(graphBuff); + if (expectedNumChars != numCharsForGraphEncoding) + { + sprintf(messageContents, "Invalid number of bytes for graph of order %d; got %d but expected %d\n", graphOrder, numCharsForGraphEncoding, expectedNumChars); + ErrorMessage(messageContents); + return NOTOK; + } + + // Check that characters are valid ASCII characters between 62 and 126 + for (int i = 0; i < numChars; i++) + { + if (graphBuff[i] < 63 || graphBuff[i] > 126) + { + sprintf(messageContents, "Invalid character at index %d: \"%c\"\n", i, graphBuff[i]); + ErrorMessage(messageContents); + return NOTOK; + } + } + + // Check that there are no extraneous bits in representation (since we pad out to a + // multiple of 6 before splitting into bytes and adding 63 to each byte) + int expectedNumPaddingZeroes = _getExpectedNumPaddingZeroes(graphOrder, numChars); + char finalByte = graphBuff[numChars - 1] - 63; + int numPaddingZeroes = 0; + for (int i = 0; i < expectedNumPaddingZeroes; i++) + { + if (finalByte & (1 << i)) + break; + + numPaddingZeroes++; + } + + if (numPaddingZeroes != expectedNumPaddingZeroes) + { + sprintf(messageContents, "Expected %d padding zeroes, but got %d.\n", expectedNumPaddingZeroes, numPaddingZeroes); + ErrorMessage(messageContents); + exitCode = NOTOK; + } + + return exitCode; +} + +// Takes the character array graphBuff, the derived number of vertices graphOrder, +// and the numChars corresponding to the number of characters after the first byte +// and performs the inverse transformation of the graph encoding: we subtract 63 from +// each byte, then only process the 6 least significant bits of the resulting byte. For +// the final byte, we determine how many padding zeroes to expect, and exclude them +// from being processed. We index into the adjacency matrix by row and column, which +// are incremented such that row ranges from 0 to one less than the column index. +int _decodeGraph(char *graphBuff, const int graphOrder, const int numChars, graphP pGraph) +{ + int exitCode = OK; + + if (pGraph == NULL) + { + ErrorMessage("Must initialize graph datastructure before invoking _decodeGraph.\n"); + return NOTOK; + } + + int numPaddingZeroes = _getExpectedNumPaddingZeroes(graphOrder, numChars); + + char currByte = '\0'; + int bitValue = 0; + int row = 0; + int col = 1; + for (int i = 0; i < numChars; i++) + { + currByte = graphBuff[i] - 63; + // j corresponds to the number of places one must bitshift the byte by to read + // the next bit in the byte + for (int j = sizeof(char) * 5; j >= 0; j--) + { + // If we are on the final byte, we know that the final numPaddingZeroes bits + // can be ignored, so we break out of the loop + if ((i == numChars) && j == numPaddingZeroes - 1) + break; + + if (row == col) + { + row = 0; + col++; + } + + bitValue = ((currByte >> j) & 1u) ? 1 : 0; + if (bitValue == 1) + { + // Add gp_GetFirstVertex(pGraph), which is 1 if NIL == 0 (i.e. internal 1-based labelling) and 0 if NIL == -1 (internally 0-based) + exitCode = gp_DynamicAddEdge(pGraph, row + gp_GetFirstVertex(pGraph), 0, col + gp_GetFirstVertex(pGraph), 0); + if (exitCode != OK) + return exitCode; + } + + row++; + } + } + + return exitCode; +} + +int endG6ReadIteration(G6ReadIterator *pG6ReadIterator) +{ + int exitCode = OK; + + if (pG6ReadIterator != NULL) + { + if (pG6ReadIterator->g6Input != NULL) + sf_Free(&(pG6ReadIterator->g6Input)); + + if (pG6ReadIterator->currGraphBuff != NULL) + { + free(pG6ReadIterator->currGraphBuff); + pG6ReadIterator->currGraphBuff = NULL; + } + } + + return exitCode; +} + +int freeG6ReadIterator(G6ReadIterator **ppG6ReadIterator) +{ + int exitCode = OK; + + if (ppG6ReadIterator != NULL && (*ppG6ReadIterator) != NULL) + { + if ((*ppG6ReadIterator)->g6Input != NULL) + sf_Free(&((*ppG6ReadIterator)->g6Input)); + + (*ppG6ReadIterator)->numGraphsRead = 0; + (*ppG6ReadIterator)->graphOrder = 0; + + if ((*ppG6ReadIterator)->currGraphBuff != NULL) + { + free((*ppG6ReadIterator)->currGraphBuff); + (*ppG6ReadIterator)->currGraphBuff = NULL; + } + + (*ppG6ReadIterator)->currGraph = NULL; + + free((*ppG6ReadIterator)); + (*ppG6ReadIterator) = NULL; + } + + return exitCode; +} + +int _ReadGraphFromG6FilePath(graphP pGraphToRead, char *pathToG6File) +{ + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + messageContents[MAXLINE] = '\0'; + int charsAvailForStr = 0; + + strOrFileP inputContainer = sf_New(NULL, pathToG6File, READTEXT); + if (inputContainer == NULL) + { + messageFormat = "Unable to allocate strOrFile container for infile \"%.*s\".\n"; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForStr, pathToG6File); + ErrorMessage(messageContents); + + return NOTOK; + } + + return _ReadGraphFromG6StrOrFile(pGraphToRead, inputContainer); +} + +int _ReadGraphFromG6String(graphP pGraphToRead, char *g6EncodedString) +{ + if (g6EncodedString == NULL || strlen(g6EncodedString) == 0) + { + ErrorMessage("Unable to proceed with empty .g6 input string.\n"); + return NOTOK; + } + + strOrFileP inputContainer = sf_New(g6EncodedString, NULL, READTEXT); + if (inputContainer == NULL) + { + ErrorMessage("Unable to allocate strOrFile container for .g6 input string.\n"); + return NOTOK; + } + + return _ReadGraphFromG6StrOrFile(pGraphToRead, inputContainer); +} + +int _ReadGraphFromG6StrOrFile(graphP pGraphToRead, strOrFileP g6InputContainer) +{ + int exitCode = OK; + + G6ReadIterator *pG6ReadIterator = NULL; + + if (sf_ValidateStrOrFile(g6InputContainer) != OK) + { + ErrorMessage("Invalid G6 output container.\n"); + return NOTOK; + } + + if (allocateG6ReadIterator(&pG6ReadIterator, pGraphToRead) != OK) + { + ErrorMessage("Unable to allocate G6ReadIterator.\n"); + return NOTOK; + } + + if (beginG6ReadIterationFromG6StrOrFile(pG6ReadIterator, g6InputContainer) != OK) + { + ErrorMessage("Unable to begin .g6 read iteration.\n"); + + if (freeG6ReadIterator(&pG6ReadIterator) != OK) + ErrorMessage("Unable to free G6ReadIterator.\n"); + + return NOTOK; + } + + exitCode = readGraphUsingG6ReadIterator(pG6ReadIterator); + if (exitCode != OK) + ErrorMessage("Unable to read graph from .g6 read iterator.\n"); + + int endG6ReadIterationCode = endG6ReadIteration(pG6ReadIterator); + if (endG6ReadIterationCode != OK) + { + ErrorMessage("Unable to end G6ReadIterator.\n"); + exitCode = endG6ReadIterationCode; + } + + int freeG6ReadIteratorCode = freeG6ReadIterator(&pG6ReadIterator); + if (freeG6ReadIteratorCode != OK) + { + ErrorMessage("Unable to free G6ReadIterator.\n"); + exitCode = freeG6ReadIteratorCode; + } + + return exitCode; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.h new file mode 100644 index 000000000..3d674502f --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef G6_READ_ITERATOR +#define G6_READ_ITERATOR + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "../graph.h" +#include "strOrFile.h" + + typedef struct + { + strOrFileP g6Input; + int numGraphsRead; + + int graphOrder; + int numCharsForGraphOrder; + int numCharsForGraphEncoding; + int currGraphBuffSize; + char *currGraphBuff; + + graphP currGraph; + } G6ReadIterator; + + int allocateG6ReadIterator(G6ReadIterator **, graphP); + bool _isG6ReadIteratorAllocated(G6ReadIterator *); + + int getNumGraphsRead(G6ReadIterator *, int *); + int getOrderOfGraphToRead(G6ReadIterator *, int *); + int getPointerToGraphReadIn(G6ReadIterator *, graphP *); + + int beginG6ReadIterationFromG6StrOrFile(G6ReadIterator *, strOrFileP); + int _beginG6ReadIteration(G6ReadIterator *); + int _processAndCheckHeader(strOrFileP); + bool _firstCharIsValid(char, const int); + int _getGraphOrder(strOrFileP, int *); + + int readGraphUsingG6ReadIterator(G6ReadIterator *); + int _checkGraphOrder(char *, int); + int _validateGraphEncoding(char *, const int, const int); + int _decodeGraph(char *, const int, const int, graphP); + + int endG6ReadIteration(G6ReadIterator *); + + int freeG6ReadIterator(G6ReadIterator **); + + int _ReadGraphFromG6FilePath(graphP, char *); + int _ReadGraphFromG6String(graphP, char *); + int _ReadGraphFromG6StrOrFile(graphP, strOrFileP); + +#ifdef __cplusplus +} +#endif + +#endif /* G6_READ_ITERATOR */ diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c new file mode 100644 index 000000000..4f8fa9bf1 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c @@ -0,0 +1,567 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include + +#include "g6-write-iterator.h" +#include "g6-api-utilities.h" + +int allocateG6WriteIterator(G6WriteIterator **ppG6WriteIterator, graphP pGraph) +{ + int exitCode = OK; + + if (ppG6WriteIterator != NULL && (*ppG6WriteIterator) != NULL) + { + ErrorMessage("G6WriteIterator is not NULL and therefore can't be allocated.\n"); + return NOTOK; + } + + // numGraphsWritten, graphOrder, numCharsForGraphOrder, + // numCharsForGraphEncoding, and currGraphBuffSize all set to 0 + (*ppG6WriteIterator) = (G6WriteIterator *)calloc(1, sizeof(G6WriteIterator)); + + if ((*ppG6WriteIterator) == NULL) + { + ErrorMessage("Unable to allocate memory for G6WriteIterator.\n"); + return NOTOK; + } + + (*ppG6WriteIterator)->g6Output = NULL; + (*ppG6WriteIterator)->currGraphBuff = NULL; + (*ppG6WriteIterator)->columnOffsets = NULL; + + if (pGraph == NULL || pGraph->N <= 0) + { + ErrorMessage("[ERROR] Must allocate and initialize graph with an order greater than 0 to use the G6WriteIterator.\n"); + + exitCode = freeG6WriteIterator(ppG6WriteIterator); + + if (exitCode != OK) + ErrorMessage("Unable to free the G6WriteIterator.\n"); + } + else + (*ppG6WriteIterator)->currGraph = pGraph; + + return exitCode; +} + +bool _isG6WriteIteratorAllocated(G6WriteIterator *pG6WriteIterator) +{ + bool G6WriteIteratorIsAllocated = true; + + if (pG6WriteIterator == NULL || pG6WriteIterator->currGraph == NULL) + G6WriteIteratorIsAllocated = false; + + return G6WriteIteratorIsAllocated; +} + +int getNumGraphsWritten(G6WriteIterator *pG6WriteIterator, int *pNumGraphsRead) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*pNumGraphsRead) = pG6WriteIterator->numGraphsWritten; + + return OK; +} + +int getOrderOfGraphToWrite(G6WriteIterator *pG6WriteIterator, int *pGraphOrder) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*pGraphOrder) = pG6WriteIterator->graphOrder; + + return OK; +} + +int getPointerToGraphToWrite(G6WriteIterator *pG6WriteIterator, graphP *ppGraph) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("[ERROR] G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*ppGraph) = pG6WriteIterator->currGraph; + + return OK; +} + +int getGraphBuff(G6WriteIterator *pG6WriteIterator, char **ppCurrGraphBuff) +{ + if (pG6WriteIterator == NULL) + { + ErrorMessage("G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + (*ppCurrGraphBuff) = pG6WriteIterator->currGraphBuff; + + return OK; +} + +int beginG6WriteIterationToG6StrOrFile(G6WriteIterator *pG6WriteIterator, strOrFileP outputContainer) +{ + int exitCode = OK; + + if (sf_ValidateStrOrFile(outputContainer) != OK) + { + ErrorMessage("Invalid strOrFile output container provided.\n"); + return NOTOK; + } + + pG6WriteIterator->g6Output = outputContainer; + + exitCode = _beginG6WriteIteration(pG6WriteIterator); + + if (exitCode != OK) + ErrorMessage("Unable to begin .g6 write iteration to given strOrFile output container.\n"); + + return exitCode; +} + +int _beginG6WriteIteration(G6WriteIterator *pG6WriteIterator) +{ + int exitCode = OK; + + char *g6Header = ">>graph6<<"; + if (sf_fputs(g6Header, pG6WriteIterator->g6Output) < 0) + { + ErrorMessage("Unable to fputs header to g6Output.\n"); + return NOTOK; + } + + pG6WriteIterator->graphOrder = pG6WriteIterator->currGraph->N; + + pG6WriteIterator->columnOffsets = (int *)calloc(pG6WriteIterator->graphOrder + 1, sizeof(int)); + + if (pG6WriteIterator->columnOffsets == NULL) + { + ErrorMessage("Unable to allocate memory for column offsets.\n"); + return NOTOK; + } + + _precomputeColumnOffsets(pG6WriteIterator->columnOffsets, pG6WriteIterator->graphOrder); + + pG6WriteIterator->numCharsForGraphOrder = _getNumCharsForGraphOrder(pG6WriteIterator->graphOrder); + pG6WriteIterator->numCharsForGraphEncoding = _getNumCharsForGraphEncoding(pG6WriteIterator->graphOrder); + // Must add 3 bytes for newline, possible carriage return, and null terminator + pG6WriteIterator->currGraphBuffSize = pG6WriteIterator->numCharsForGraphOrder + pG6WriteIterator->numCharsForGraphEncoding + 3; + pG6WriteIterator->currGraphBuff = (char *)calloc(pG6WriteIterator->currGraphBuffSize, sizeof(char)); + + if (pG6WriteIterator->currGraphBuff == NULL) + { + ErrorMessage("Unable to allocate memory for currGraphBuff.\n"); + exitCode = NOTOK; + } + + return exitCode; +} + +void _precomputeColumnOffsets(int *columnOffsets, int graphOrder) +{ + if (columnOffsets == NULL) + { + ErrorMessage("Must allocate columnOffsets memory before precomputation.\n"); + return; + } + + columnOffsets[0] = 0; + columnOffsets[1] = 0; + for (int i = 2; i <= graphOrder; i++) + columnOffsets[i] = columnOffsets[i - 1] + (i - 1); +} + +int writeGraphUsingG6WriteIterator(G6WriteIterator *pG6WriteIterator) +{ + int exitCode = OK; + + if (!_isG6WriteIteratorAllocated(pG6WriteIterator)) + { + ErrorMessage("Unable to write graph, as G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + exitCode = _encodeAdjMatAsG6(pG6WriteIterator); + + if (exitCode != OK) + { + ErrorMessage("Error converting adjacency matrix to g6 format.\n"); + return exitCode; + } + + exitCode = _printEncodedGraph(pG6WriteIterator); + if (exitCode != OK) + ErrorMessage("Unable to output g6 encoded graph to string-or-file container.\n"); + + return exitCode; +} + +int _encodeAdjMatAsG6(G6WriteIterator *pG6WriteIterator) +{ + int exitCode = OK; + + if (!_isG6WriteIteratorAllocated(pG6WriteIterator)) + { + ErrorMessage("Unable to encode graph, as G6WriteIterator is not allocated.\n"); + return NOTOK; + } + + char *g6Encoding = pG6WriteIterator->currGraphBuff; + + if (g6Encoding == NULL) + { + ErrorMessage("[ERROR] Graph buffer is not allocated.\n"); + return NOTOK; + } + + int *columnOffsets = pG6WriteIterator->columnOffsets; + if (columnOffsets == NULL) + { + ErrorMessage("Column offsets array is not allocated.\n"); + return NOTOK; + } + + graphP pGraph = pG6WriteIterator->currGraph; + + if (pGraph == NULL || pGraph->N == 0) + { + ErrorMessage("Graph is not allocated.\n"); + return NOTOK; + } + + // memset ensures all bits are zero, which means we only need to set the bits + // that correspond to an edge; this also takes care of padding zeroes for us + memset(pG6WriteIterator->currGraphBuff, 0, (pG6WriteIterator->currGraphBuffSize) * sizeof(char)); + + int graphOrder = pG6WriteIterator->graphOrder; + int numCharsForGraphOrder = pG6WriteIterator->numCharsForGraphOrder; + int numCharsForGraphEncoding = pG6WriteIterator->numCharsForGraphEncoding; + int totalNumCharsForOrderAndGraph = numCharsForGraphOrder + numCharsForGraphEncoding; + + if (graphOrder > 62) + { + g6Encoding[0] = 126; + // bytes 1 through 3 will be populated with the 18-bit representation of the graph order + int intermediate = -1; + for (int i = 0; i < 3; i++) + { + intermediate = graphOrder >> (6 * i); + g6Encoding[3 - i] = intermediate & 63; + g6Encoding[3 - i] += 63; + } + } + else if (graphOrder > 1 && graphOrder < 63) + { + g6Encoding[0] = (char)(graphOrder + 63); + } + + int u = NIL, v = NIL, e = NIL; + exitCode = _getFirstEdge(pGraph, &e, &u, &v); + + if (exitCode != OK) + { + ErrorMessage("Unable to fetch first edge in graph.\n"); + return exitCode; + } + + int charOffset = 0; + int bitPositionPower = 0; + while (u != NIL && v != NIL) + { + // The internal graph representation is usually 1-based, but may be 0-based, so + // one must subtract the index of the first vertex (i.e. result of gp_GetFirstVertex) + // because the .g6 format is 0-based + u -= gp_GetFirstVertex(theGraph); + v -= gp_GetFirstVertex(theGraph); + + // The columnOffset machinery assumes that we are traversing the edges represented in + // the upper-triangular matrix. Since we are dealing with simple graphs, if (v, u) + // exists, then (u, v) exists, and so the edge is indicated by a 1 in row = min(u, v) + // and col = max(u, v) in the upper-triangular adjacency matrix. + if (v < u) + { + int tempVert = v; + v = u; + u = tempVert; + } + + // (columnOffsets[v] + u) describes the bit index of the current edge + // given the column and row in the adjacency matrix representation; + // the byte is floor((columnOffsets[v] + u) / 6) and the we determine which + // bit to set in that byte by left-shifting 1 by (5 - ((columnOffsets[v] + u) % 6)) + // (transforming the ((columnOffsets[v] + u) % 6)th bit from the left to the + // (5 - ((columnOffsets[v] + u) % 6))th bit from the right) + charOffset = numCharsForGraphOrder + ((columnOffsets[v] + u) / 6); + bitPositionPower = 5 - ((columnOffsets[v] + u) % 6); + + g6Encoding[charOffset] |= (1u << bitPositionPower); + + exitCode = _getNextEdge(pGraph, &e, &u, &v); + + if (exitCode != OK) + { + ErrorMessage("Unable to fetch next edge in graph.\n"); + free(columnOffsets); + free(g6Encoding); + return exitCode; + } + } + + // Bytes corresponding to graph order have already been modified to + // correspond to printable ascii character (i.e. by adding 63); must + // now do the same for bytes corresponding to edge lists + for (int i = numCharsForGraphOrder; i < totalNumCharsForOrderAndGraph; i++) + g6Encoding[i] += 63; + + return exitCode; +} + +int _getFirstEdge(graphP theGraph, int *e, int *u, int *v) +{ + if (theGraph == NULL) + return NOTOK; + + if ((*e) >= gp_EdgeInUseIndexBound(theGraph)) + { + ErrorMessage("First edge is outside bounds."); + return NOTOK; + } + + (*e) = gp_GetFirstEdge(theGraph); + + return _getNextInUseEdge(theGraph, e, u, v); +} + +int _getNextEdge(graphP theGraph, int *e, int *u, int *v) +{ + if (theGraph == NULL) + return NOTOK; + + (*e) += 2; + + return _getNextInUseEdge(theGraph, e, u, v); +} + +int _getNextInUseEdge(graphP theGraph, int *e, int *u, int *v) +{ + int exitCode = OK; + int EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + + (*u) = NIL; + (*v) = NIL; + + if ((*e) < EsizeOccupied) + { + while (!gp_EdgeInUse(theGraph, (*e))) + { + (*e) += 2; + if ((*e) >= EsizeOccupied) + break; + } + + if ((*e) < EsizeOccupied && gp_EdgeInUse(theGraph, (*e))) + { + (*u) = gp_GetNeighbor(theGraph, (*e)); + (*v) = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, (*e))); + } + } + + return exitCode; +} + +int _printEncodedGraph(G6WriteIterator *pG6WriteIterator) +{ + int exitCode = OK; + + if (pG6WriteIterator->g6Output == NULL) + { + ErrorMessage("Unable to print to NULL string-or-file container.\n"); + return NOTOK; + } + + if (pG6WriteIterator->currGraphBuff == NULL || strlen(pG6WriteIterator->currGraphBuff) == 0) + { + ErrorMessage("Unable to print; g6 encoding is empty.\n"); + return NOTOK; + } + + if (sf_fputs(pG6WriteIterator->currGraphBuff, pG6WriteIterator->g6Output) < 0) + { + ErrorMessage("Failed to output all characters of g6 encoding.\n"); + exitCode = NOTOK; + } + + if (sf_fputs("\n", pG6WriteIterator->g6Output) < 0) + { + ErrorMessage("Failed to put line terminator after g6 encoding.\n"); + exitCode = NOTOK; + } + + return exitCode; +} + +int endG6WriteIteration(G6WriteIterator *pG6WriteIterator) +{ + int exitCode = OK; + + if (pG6WriteIterator != NULL) + { + if (pG6WriteIterator->g6Output != NULL) + sf_Free(&(pG6WriteIterator->g6Output)); + + if (pG6WriteIterator->currGraphBuff != NULL) + { + free(pG6WriteIterator->currGraphBuff); + pG6WriteIterator->currGraphBuff = NULL; + } + + if (pG6WriteIterator->columnOffsets != NULL) + { + free((pG6WriteIterator->columnOffsets)); + pG6WriteIterator->columnOffsets = NULL; + } + } + + return exitCode; +} + +int freeG6WriteIterator(G6WriteIterator **ppG6WriteIterator) +{ + int exitCode = OK; + + if (ppG6WriteIterator != NULL && (*ppG6WriteIterator) != NULL) + { + if ((*ppG6WriteIterator)->g6Output != NULL) + sf_Free(&((*ppG6WriteIterator)->g6Output)); + + (*ppG6WriteIterator)->numGraphsWritten = 0; + (*ppG6WriteIterator)->graphOrder = 0; + + if ((*ppG6WriteIterator)->currGraphBuff != NULL) + { + free((*ppG6WriteIterator)->currGraphBuff); + (*ppG6WriteIterator)->currGraphBuff = NULL; + } + + if ((*ppG6WriteIterator)->columnOffsets != NULL) + { + free(((*ppG6WriteIterator)->columnOffsets)); + (*ppG6WriteIterator)->columnOffsets = NULL; + } + + // N.B. The G6WriteIterator doesn't "own" the graph, so we don't free it. + (*ppG6WriteIterator)->currGraph = NULL; + + free((*ppG6WriteIterator)); + (*ppG6WriteIterator) = NULL; + } + + return exitCode; +} + +int _WriteGraphToG6FilePath(graphP pGraph, char *g6OutputFilename) +{ + strOrFileP outputContainer = sf_New(NULL, g6OutputFilename, WRITETEXT); + if (outputContainer == NULL) + { + ErrorMessage("Unable to allocate outputContainer to which to write.\n"); + return NOTOK; + } + + return _WriteGraphToG6StrOrFile(pGraph, outputContainer, NULL); +} + +int _WriteGraphToG6String(graphP pGraph, char **g6OutputStr) +{ + strOrFileP outputContainer = sf_New(NULL, NULL, WRITETEXT); + if (outputContainer == NULL) + { + ErrorMessage("Unable to allocate outputContainer to which to write.\n"); + return NOTOK; + } + + return _WriteGraphToG6StrOrFile(pGraph, outputContainer, g6OutputStr); +} + +int _WriteGraphToG6StrOrFile(graphP pGraph, strOrFileP outputContainer, char **outputStr) +{ + int exitCode = OK; + + G6WriteIterator *pG6WriteIterator = NULL; + + if (sf_ValidateStrOrFile(outputContainer) != OK) + { + ErrorMessage("Invalid G6 output container.\n"); + return NOTOK; + } + + if (outputContainer->theStr != NULL && (outputStr == NULL)) + { + ErrorMessage("If writing G6 to string, must provide pointer-pointer " + "to allow _WriteGraphToG6StrOrFile() to assign the address " + "of the output string.\n"); + return NOTOK; + } + + if (outputStr != NULL && (*outputStr) != NULL) + { + ErrorMessage("(*outputStr) should not point to allocated memory."); + return NOTOK; + } + + exitCode = allocateG6WriteIterator(&pG6WriteIterator, pGraph); + + if (exitCode != OK) + { + ErrorMessage("Unable to allocate G6WriteIterator.\n"); + freeG6WriteIterator(&pG6WriteIterator); + return exitCode; + } + + exitCode = beginG6WriteIterationToG6StrOrFile(pG6WriteIterator, outputContainer); + + if (exitCode != OK) + { + ErrorMessage("Unable to begin G6 write iteration.\n"); + freeG6WriteIterator(&pG6WriteIterator); + return exitCode; + } + + exitCode = writeGraphUsingG6WriteIterator(pG6WriteIterator); + + if (exitCode != OK) + ErrorMessage("Unable to write graph using G6WriteIterator.\n"); + else + { + if (outputStr != NULL && pG6WriteIterator->g6Output->theStr != NULL) + (*outputStr) = sf_takeTheStr(pG6WriteIterator->g6Output); + } + + int endG6WriteIterationCode = endG6WriteIteration(pG6WriteIterator); + if (endG6WriteIterationCode != OK) + { + ErrorMessage("Unable to end G6 write iteration.\n"); + exitCode = endG6WriteIterationCode; + } + + int freeG6WriteIteratorCode = freeG6WriteIterator(&pG6WriteIterator); + if (freeG6WriteIteratorCode != OK) + { + ErrorMessage("Unable to free G6Writer.\n"); + exitCode = freeG6WriteIteratorCode; + } + + return exitCode; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.h new file mode 100644 index 000000000..76a58b0e1 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.h @@ -0,0 +1,69 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef G6_WRITE_ITERATOR +#define G6_WRITE_ITERATOR + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include "../graph.h" +#include "strOrFile.h" + + typedef struct + { + strOrFileP g6Output; + int numGraphsWritten; + + int graphOrder; + int numCharsForGraphOrder; + int numCharsForGraphEncoding; + int currGraphBuffSize; + char *currGraphBuff; + + int *columnOffsets; + + graphP currGraph; + } G6WriteIterator; + + int allocateG6WriteIterator(G6WriteIterator **, graphP); + bool _isG6WriteIteratorAllocated(G6WriteIterator *); + + int getNumGraphsWritten(G6WriteIterator *, int *); + int getOrderOfGraphToWrite(G6WriteIterator *, int *); + int getPointerToGraphToWrite(G6WriteIterator *, graphP *); + + int beginG6WriteIterationToG6StrOrFile(G6WriteIterator *pG6WriteIterator, strOrFileP outputContainer); + int _beginG6WriteIteration(G6WriteIterator *pG6WriteIterator); + void _precomputeColumnOffsets(int *, int); + + int writeGraphUsingG6WriteIterator(G6WriteIterator *); + + int _encodeAdjMatAsG6(G6WriteIterator *); + int _getFirstEdge(graphP, int *, int *, int *); + int _getNextEdge(graphP, int *, int *, int *); + int _getNextInUseEdge(graphP theGraph, int *e, int *u, int *v); + + int _printEncodedGraph(G6WriteIterator *); + + int endG6WriteIteration(G6WriteIterator *); + + int freeG6WriteIterator(G6WriteIterator **); + + int _WriteGraphToG6FilePath(graphP pGraph, char *g6OutputFilename); + int _WriteGraphToG6String(graphP pGraph, char **g6OutputStr); + int _WriteGraphToG6StrOrFile(graphP pGraph, strOrFileP outputContainer, char **outputStr); + +#ifdef __cplusplus +} +#endif + +#endif /* G6_WRITE_ITERATOR */ diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c new file mode 100644 index 000000000..510529916 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c @@ -0,0 +1,1067 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include +#include + +#include "../graph.h" + +/* Private functions (exported to system) */ + +int _ReadGraph(graphP theGraph, strOrFileP inputContainer); +int _ReadAdjMatrix(graphP theGraph, strOrFileP inputContainer); +int _ReadAdjList(graphP theGraph, strOrFileP inputContainer); +int _ReadLEDAGraph(graphP theGraph, strOrFileP inputContainer); + +int _WriteGraph(graphP theGraph, strOrFileP *outputContainer, char **pOutputStr, int Mode); +int _WriteAdjList(graphP theGraph, strOrFileP outputContainer); +int _WriteAdjMatrix(graphP theGraph, strOrFileP outputContainer); +int _WriteDebugInfo(graphP theGraph, strOrFileP outputContainer); + +/******************************************************************** + _ReadAdjMatrix() + + This function reads the undirected graph in upper triangular matrix format. + Though O(N^2) time is required, this routine is useful during + reliability testing due to the wealth of graph generating software + that uses this format for output. + Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges + ********************************************************************/ + +int _ReadAdjMatrix(graphP theGraph, strOrFileP inputContainer) + +{ + int N = -1; + int v, w, Flag; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + // Read the number of vertices from the first line of the file + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (sf_ReadInteger(&N, inputContainer) != OK) + return NOTOK; + + // Initialize the graph based on the number of vertices + if (gp_InitGraph(theGraph, N) != OK) + return NOTOK; + + // Read an upper-triangular matrix row for each vertex + // Note that for the last vertex, zero flags are read, per the upper triangular format + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + gp_SetVertexIndex(theGraph, v, v); + for (w = v + 1; gp_VertexInRange(theGraph, w); w++) + { + // Read each of v's w-neighbor flags + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (sf_ReadSingleDigit(&Flag, inputContainer) != OK) + return NOTOK; + // N.B. Currently do not allow edge-weights in Adjacency + // Matrix format + if (Flag != 0 && Flag != 1) + return NOTOK; + + // Add the edge (v, w) if the flag is raised + if (Flag) + { + if (gp_DynamicAddEdge(theGraph, v, 0, w, 0) != OK) + return NOTOK; + } + } + } + + return OK; +} + +/******************************************************************** + _ReadAdjList() + This function reads the graph in adjacency list format. + + The graph format is: + On the first line : N= + On N subsequent lines: #: a b c ... -1 + where # is a vertex number and a, b, c, ... are its neighbors. + + NOTE: The vertex number is for file documentation only. It is an + error if the vertices are not in sorted order in the file. + + NOTE: If a loop edge is found, it is ignored without error. + + NOTE: This routine supports digraphs. For a directed arc (v -> W), + an edge record is created in both vertices, v and W, and the + edge record in v's adjacency list is marked OUTONLY while the + edge record in W's list is marked INONLY. + This makes it easy to used edge directedness when appropriate + but also seamlessly process the corresponding undirected graph. + + Returns: OK on success, NONEMBEDDABLE if success except too many edges + NOTOK on file content error (or internal error) + ********************************************************************/ + +int _ReadAdjList(graphP theGraph, strOrFileP inputContainer) +{ + int N = -1; + int v, W, adjList, e, indexValue, ErrorCode; + int zeroBased = FALSE; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + // Skip the "N=" and then read the N value for number of vertices + if (sf_ReadSkipChar(inputContainer) != OK) + return NOTOK; + + if (sf_ReadSkipChar(inputContainer) != OK) + return NOTOK; + + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (sf_ReadInteger(&N, inputContainer) != OK) + return NOTOK; + + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + // Initialize theGraph based on the number of vertices in the input + if (gp_InitGraph(theGraph, N) != OK) + return NOTOK; + + // Clear the visited members of the vertices so they can be used + // during the adjacency list read operation + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + gp_SetVertexVisitedInfo(theGraph, v, NIL); + + // Do the adjacency list read operation for each vertex in order + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + // Read the vertex number + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&indexValue, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (indexValue == 0 && v == gp_GetFirstVertex(theGraph)) + zeroBased = TRUE; + indexValue += zeroBased ? gp_GetFirstVertex(theGraph) : 0; + + gp_SetVertexIndex(theGraph, v, indexValue); + + // The vertices are expected to be in numeric ascending order + if (gp_GetVertexIndex(theGraph, v) != v) + return NOTOK; + + // Skip the colon after the vertex number + if (sf_ReadSkipChar(inputContainer) != OK) + return NOTOK; + + // If the vertex already has a non-empty adjacency list, then it is + // the result of adding edges during processing of preceding vertices. + // The list is removed from the current vertex v and saved for use + // during the read operation for v. Adjacencies to preceding vertices + // are pulled from this list, if present, or added as directed edges + // if not. Adjacencies to succeeding vertices are added as undirected + // edges, and will be corrected later if the succeeding vertex does not + // have the matching adjacency using the following mechanism. After the + // read operation for a vertex v, any adjacency nodes left in the saved + // list are converted to directed edges from the preceding vertex to v. + adjList = gp_GetFirstArc(theGraph, v); + if (gp_IsArc(adjList)) + { + // Store the adjacency node location in the visited member of each + // of the preceding vertices to which v is adjacent so that we can + // efficiently detect the adjacency during the read operation and + // efficiently find the adjacency node. + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), e); + e = gp_GetNextArc(theGraph, e); + } + + // Make the adjacency list circular, for later ease of processing + gp_SetPrevArc(theGraph, adjList, gp_GetLastArc(theGraph, v)); + gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), adjList); + + // Remove the list from the vertex + gp_SetFirstArc(theGraph, v, NIL); + gp_SetLastArc(theGraph, v, NIL); + } + + // Read the adjacency list. + while (1) + { + // Read the value indicating the next adjacent vertex (or the list end) + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&W, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + W += zeroBased ? gp_GetFirstVertex(theGraph) : 0; + + // A value below the valid range indicates the adjacency list end + if (W < gp_GetFirstVertex(theGraph)) + break; + + // A value above the valid range is an error + if (W > gp_GetLastVertex(theGraph)) + return NOTOK; + + // Loop edges are not supported + else if (W == v) + return NOTOK; + + // If the adjacency is to a succeeding, higher numbered vertex, + // then we'll add an undirected edge for now + else if (v < W) + { + if ((ErrorCode = gp_DynamicAddEdge(theGraph, v, 0, W, 0)) != OK) + return ErrorCode; + } + + // If the adjacency is to a preceding, lower numbered vertex, then + // we have to pull the adjacency node from the preexisting adjList, + // if it is there, and if not then we have to add a directed edge. + else + { + // If the adjacency node (arc) already exists, then we add it + // as the new first arc of the vertex and delete it from adjList + if (gp_IsArc(gp_GetVertexVisitedInfo(theGraph, W))) + { + e = gp_GetVertexVisitedInfo(theGraph, W); + + // Remove the arc e from the adjList construct + gp_SetVertexVisitedInfo(theGraph, W, NIL); + if (adjList == e) + { + if ((adjList = gp_GetNextArc(theGraph, e)) == e) + adjList = NIL; + } + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e)); + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e)); + + gp_AttachFirstArc(theGraph, v, e); + } + + // If an adjacency node to the lower numbered vertex W does not + // already exist, then we make a new directed arc from the current + // vertex v to W. + else + { + // It is added as the new first arc in both vertices + if ((ErrorCode = gp_DynamicAddEdge(theGraph, v, 0, W, 0)) != OK) + return ErrorCode; + + // Note that this call also sets OUTONLY on the twin arc + gp_SetDirection(theGraph, gp_GetFirstArc(theGraph, W), EDGEFLAG_DIRECTION_INONLY); + } + } + } + + // If there are still adjList entries after the read operation + // then those entries are not representative of full undirected edges. + // Rather, they represent incoming directed arcs from other vertices + // into vertex v. They need to be added back into v's adjacency list but + // marked as "INONLY", while the twin is marked "OUTONLY" (by the same function). + while (gp_IsArc(adjList)) + { + e = adjList; + + gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), NIL); + + if ((adjList = gp_GetNextArc(theGraph, e)) == e) + adjList = NIL; + + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e)); + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e)); + + gp_AttachFirstArc(theGraph, v, e); + gp_SetDirection(theGraph, e, EDGEFLAG_DIRECTION_INONLY); + } + } + + if (zeroBased) + theGraph->internalFlags |= FLAGS_ZEROBASEDIO; + + return OK; +} + +/******************************************************************** + _ReadLEDAGraph() + Reads the edge list from a LEDA file containing a simple undirected graph. + LEDA files use a one-based numbering system, which is converted to + zero-based numbers if the graph reports starting at zero as the first vertex. + + Returns: OK on success, NONEMBEDDABLE if success except too many edges + NOTOK on file content error (or internal error) + ********************************************************************/ + +int _ReadLEDAGraph(graphP theGraph, strOrFileP inputContainer) +{ + char Line[MAXLINE + 1]; + int N = -1; + int graphType, M, m, u, v, ErrorCode; + int zeroBasedOffset = gp_GetFirstVertex(theGraph) == 0 ? 1 : 0; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + /* + N.B. Skip the lines that say LEDA.GRAPH and give the node and + edge types then determine if graph is directed (-1) or + undirected (-2); we only support undirected graphs at this time. + */ + for (int i = 0; i < 3; i++) + if (sf_ReadSkipLineRemainder(inputContainer) != OK) + return NOTOK; + + if (sf_ReadInteger(&graphType, inputContainer) != OK) + return NOTOK; + + // N.B. We currently only support undirected graphs + if (graphType != -2) + return NOTOK; + + /* + Skip any preceding whitespace, read the number of vertices N, + and skip any subsequent whitespace. + */ + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&N, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + if (gp_InitGraph(theGraph, N) != OK) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (sf_fgets(Line, MAXLINE, inputContainer) == NULL) + return NOTOK; + + /* Read the number of edges */ + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&M, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + + /* Read and add each edge, omitting loops and parallel edges */ + for (m = 0; m < M; m++) + { + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&u, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipWhitespace(inputContainer) != OK) + return NOTOK; + if (sf_ReadInteger(&v, inputContainer) != OK) + return NOTOK; + if (sf_ReadSkipLineRemainder(inputContainer) != OK) + return NOTOK; + + if (u != v && !gp_IsNeighbor(theGraph, u - zeroBasedOffset, v - zeroBasedOffset)) + { + if ((ErrorCode = gp_DynamicAddEdge(theGraph, u - zeroBasedOffset, 0, v - zeroBasedOffset, 0)) != OK) + return ErrorCode; + } + } + + if (zeroBasedOffset) + theGraph->internalFlags |= FLAGS_ZEROBASEDIO; + + return OK; +} + +/******************************************************************** + gp_Read() + + Populates theGraph from the contents of the input file with path + FileName. + + Pass "stdin" for the FileName to read from the stdin stream. + + Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges + ********************************************************************/ + +int gp_Read(graphP theGraph, char *FileName) +{ + strOrFileP inputContainer = sf_New(NULL, FileName, READTEXT); + if (inputContainer == NULL) + return NOTOK; + + return _ReadGraph(theGraph, inputContainer); +} + +/******************************************************************** + gp_ReadFromString() + + Populates theGraph using the information stored in inputStr. + + The ownership of inputStr is transferred from the caller; it is + assigned to a strOrFile container and ownership is transferred to + the internal helper function _ReadGraph(), which then handles + freeing this memory. + + Returns NOTOK for any error, or OK otherwise + ********************************************************************/ + +int gp_ReadFromString(graphP theGraph, char *inputStr) +{ + strOrFileP inputContainer = sf_New(inputStr, NULL, READTEXT); + if (inputContainer == NULL) + { + if (inputStr != NULL) + free(inputStr); + inputStr = NULL; + return NOTOK; + } + + return _ReadGraph(theGraph, inputContainer); +} + +/******************************************************************** + _ReadGraph() + + Determines the graph input format by parsing the first line of the + input stream or string contained by the inputContainer and calling the + appropriate read function, then then cleans up the inputContainer and + returns the graph. + + Digraphs and loop edges are not supported in the adjacency matrix + format, which is upper triangular. + + In the adjacency list format, digraphs are supported. Loop edges are + ignored without producing an error. + ********************************************************************/ + +int _ReadGraph(graphP theGraph, strOrFileP inputContainer) +{ + int RetVal = OK; + bool extraDataAllowed = false; + char lineBuff[MAXLINE + 1]; + + if (sf_ValidateStrOrFile(inputContainer) != OK) + return NOTOK; + + if (sf_fgets(lineBuff, MAXLINE, inputContainer) == NULL) + { + sf_Free(&inputContainer); + return NOTOK; + } + + if (sf_ungets(lineBuff, inputContainer) != OK) + { + sf_Free(&inputContainer); + return NOTOK; + } + + if (strncmp(lineBuff, "LEDA.GRAPH", strlen("LEDA.GRAPH")) == 0) + { + RetVal = _ReadLEDAGraph(theGraph, inputContainer); + } + else if (strncmp(lineBuff, "N=", strlen("N=")) == 0) + { + RetVal = _ReadAdjList(theGraph, inputContainer); + if (RetVal == OK) + extraDataAllowed = true; + } + else if (isdigit(lineBuff[0])) + { + RetVal = _ReadAdjMatrix(theGraph, inputContainer); + if (RetVal == OK) + extraDataAllowed = true; + } + else + { + RetVal = _ReadGraphFromG6StrOrFile(theGraph, inputContainer); + // N.B. Unlike the other _Read functions, we are relinquishing + // ownership of inputContainer to the G6ReadIterator, which + // calls sf_Free() when ending iteration. This assignment + // prevents calling free on alread-freed memory. + inputContainer = NULL; + } + + // The possibility of "extra data" is not allowed for .g6 format: + // .g6 files may contain multiple graphs, which are not valid input + // for the extra data readers (i.e. fpReadPostProcess) Additionally, + // we don't want to add extra data if the graph is nonembeddable, as + // the FILE pointer isn't necessarily advanced past the graph + // encoding unless OK is returned. + if (extraDataAllowed) + { + char charAfterGraphRead = EOF; + if ((charAfterGraphRead = sf_getc(inputContainer)) != EOF) + { + if (sf_ungetc(charAfterGraphRead, inputContainer) != charAfterGraphRead) + RetVal = NOTOK; + else + { + strBufP extraData = sb_New(0); + if (extraData == NULL) + RetVal = NOTOK; + else + { + // FIXME: how do I distinguish between "there's no more content on input stream" and "I've hit an error state" + while (sf_fgets(lineBuff, MAXLINE, inputContainer) != NULL) + { + if (sb_ConcatString(extraData, lineBuff) != OK) + { + RetVal = NOTOK; + break; + } + } + + if (sb_GetSize(extraData) > 0) + RetVal = theGraph->functions.fpReadPostprocess(theGraph, sb_GetReadString(extraData)); + + sb_Free(&extraData); + extraData = NULL; + } + } + } + } + + if (inputContainer != NULL) + sf_Free(&inputContainer); + inputContainer = NULL; + + return RetVal; +} + +int _ReadPostprocess(graphP theGraph, char *extraData) +{ + return OK; +} + +/******************************************************************** + _WriteAdjList() + For each vertex, we write its number, a colon, the list of adjacent + vertices, then a NIL. The vertices occupy the first N positions of + theGraph. Each vertex is also has indicators of the first and last + adjacency nodes (arcs) in its adjacency list. + + Returns: NOTOK for parameter errors; OK otherwise. + ********************************************************************/ + +int _WriteAdjList(graphP theGraph, strOrFileP outputContainer) +{ + int v, e; + int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; + char numberStr[MAXCHARSFOR32BITINT + 1]; + memset(numberStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char)); + + if (theGraph == NULL || sf_ValidateStrOrFile(outputContainer) != OK) + return NOTOK; + + // Write the number of vertices of the graph to the file or string buffer + if (sprintf(numberStr, "N=%d\n", theGraph->N) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + + // Write the adjacency list of each vertex + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (sprintf(numberStr, "%d:", v - zeroBasedOffset) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + + e = gp_GetLastArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) + { + if (sprintf(numberStr, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + } + + e = gp_GetPrevArc(theGraph, e); + } + + // Write NIL at the end of the adjacency list (in zero-based I/O, NIL was -1) + if (sprintf(numberStr, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + } + + return OK; +} + +/******************************************************************** + _WriteAdjMatrix() + Outputs upper triangular matrix representation capable of being + read by _ReadAdjMatrix(). + + theGraph and one of Outfile or theStrBuf must be non-NULL. + + Note: This routine does not support digraphs and will return an + error if a directed edge is found. + + returns OK for success, NOTOK for failure + ********************************************************************/ + +int _WriteAdjMatrix(graphP theGraph, strOrFileP outputContainer) +{ + int v, e, K; + char *Row = NULL; + char numberStr[MAXCHARSFOR32BITINT + 1]; + memset(numberStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char)); + + if (theGraph == NULL || sf_ValidateStrOrFile(outputContainer) != OK) + return NOTOK; + + // Write the number of vertices in the graph to the file or string buffer + if (sprintf(numberStr, "%d\n", theGraph->N) < 1) + return NOTOK; + if (sf_fputs(numberStr, outputContainer) == EOF) + return NOTOK; + + // Allocate memory for storing a string expression of one row at a time + Row = (char *)malloc((theGraph->N + 2) * sizeof(char)); + if (Row == NULL) + return NOTOK; + + // Construct the upper triangular matrix representation one row at a time + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + for (K = gp_GetFirstVertex(theGraph); K <= v; K++) + Row[K - gp_GetFirstVertex(theGraph)] = ' '; + for (K = v + 1; gp_VertexInRange(theGraph, K); K++) + Row[K - gp_GetFirstVertex(theGraph)] = '0'; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetDirection(theGraph, e) == EDGEFLAG_DIRECTION_INONLY) + return NOTOK; + + if (gp_GetNeighbor(theGraph, e) > v) + Row[gp_GetNeighbor(theGraph, e) - gp_GetFirstVertex(theGraph)] = '1'; + + e = gp_GetNextArc(theGraph, e); + } + + Row[theGraph->N] = '\n'; + Row[theGraph->N + 1] = '\0'; + + // Write the row to the file or string buffer + if (sf_fputs(Row, outputContainer) == EOF) + return NOTOK; + } + + free(Row); + return OK; +} + +/******************************************************************** + ********************************************************************/ + +char _GetEdgeTypeChar(graphP theGraph, int e) +{ + char type = 'U'; + + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + type = 'C'; + else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_FORWARD) + type = 'F'; + else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) + type = 'P'; + else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) + type = 'B'; + else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_RANDOMTREE) + type = 'T'; + + return type; +} + +/******************************************************************** + ********************************************************************/ + +char _GetVertexObstructionTypeChar(graphP theGraph, int v) +{ + char type = 'U'; + + if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW) + type = 'X'; + else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) + type = 'x'; + if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW) + type = 'Y'; + else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) + type = 'y'; + + return type; +} + +/******************************************************************** + _WriteDebugInfo() + Writes adjacency list, but also includes the type value of each + edge (e.g. is it DFS child arc, forward arc or back arc?), and + the L, A and DFSParent of each vertex. + ********************************************************************/ + +int _WriteDebugInfo(graphP theGraph, strOrFileP outputContainer) +{ + int v, e, EsizeOccupied; + char lineBuf[MAXLINE + 1]; + memset(lineBuf, '\0', (MAXLINE + 1) * sizeof(char)); + + if (theGraph == NULL || sf_ValidateStrOrFile(outputContainer) != OK) + return NOTOK; + + /* Print parent copy vertices and their adjacency lists */ + if (sprintf(lineBuf, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (sprintf(lineBuf, "%d(P=%d,lA=%d,LowPt=%d,v=%d):", + v, gp_GetVertexParent(theGraph, v), + gp_GetVertexLeastAncestor(theGraph, v), + gp_GetVertexLowpoint(theGraph, v), + gp_GetVertexIndex(theGraph, v)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (sprintf(lineBuf, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + e = gp_GetNextArc(theGraph, e); + } + + if (sprintf(lineBuf, " %d\n", NIL) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + + /* Print any root copy vertices and their adjacency lists */ + + for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) + { + if (!gp_VirtualVertexInUse(theGraph, v)) + continue; + + if (sprintf(lineBuf, "%d(copy of=%d, DFS child=%d):", + v, gp_GetVertexIndex(theGraph, v), + gp_GetDFSChildFromRoot(theGraph, v)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (sprintf(lineBuf, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + + e = gp_GetNextArc(theGraph, e); + } + + if (sprintf(lineBuf, " %d\n", NIL) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + + /* Print information about vertices and root copy (virtual) vertices */ + if (sf_fputs("\nVERTEX INFORMATION\n", outputContainer) == EOF) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (sprintf(lineBuf, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n", + v, + gp_GetVertexIndex(theGraph, v), + (gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)), + gp_GetFirstArc(theGraph, v), + gp_GetLastArc(theGraph, v)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) + { + if (gp_VirtualVertexNotInUse(theGraph, v)) + continue; + + if (sprintf(lineBuf, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n", + v, + gp_GetVertexIndex(theGraph, v), + (gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)), + gp_GetFirstArc(theGraph, v), + gp_GetLastArc(theGraph, v)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + + /* Print information about edges */ + + if (sf_fputs("\nEDGE INFORMATION\n", outputContainer) == EOF) + return NOTOK; + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) + { + if (gp_EdgeInUse(theGraph, e)) + { + if (sprintf(lineBuf, "E[%3d] neighbor=%3d, type=%c, next arc=%3d, prev arc=%3d\n", + e, + gp_GetNeighbor(theGraph, e), + _GetEdgeTypeChar(theGraph, e), + gp_GetNextArc(theGraph, e), + gp_GetPrevArc(theGraph, e)) < 1) + return NOTOK; + if (sf_fputs(lineBuf, outputContainer) == EOF) + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + gp_Write() + Writes theGraph into the file. + Pass "stdout" or "stderr" to FileName to write to the corresponding stream + Pass WRITE_G6, WRITE_ADJLIST, WRITE_ADJMATRIX, or WRITE_DEBUGINFO for the Mode + + NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST + + Returns NOTOK on error, OK on success. + ********************************************************************/ + +int gp_Write(graphP theGraph, char *FileName, int Mode) +{ + int RetVal; + + if (theGraph == NULL || FileName == NULL) + return NOTOK; + + if (strcmp(FileName, "nullwrite") == 0) + return OK; + + strOrFileP outputContainer = sf_New(NULL, FileName, WRITETEXT); + if (outputContainer == NULL) + return NOTOK; + + RetVal = _WriteGraph(theGraph, &outputContainer, NULL, Mode); + + if (outputContainer != NULL) + sf_Free(&outputContainer); + outputContainer = NULL; + + return RetVal; +} + +/******************************************************************** + * gp_WriteToString() + * + * Writes the information of theGraph into a string that is returned + * to the caller via the pointer pointer pOutputStr. + * The string is owned by the caller and should be released with + * free() when the caller doesn't need the string anymore. + * The format of the content written into the returned string is based + * on the Mode parameter: WRITE_G6, WRITE_ADJLIST, or WRITE_ADJMATRIX + * (the WRITE_DEBUGINFO Mode is not supported at this time) + + NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST + + Returns NOTOK on error, or OK on success along with an allocated string + *pOutputStr that the caller must free() + ********************************************************************/ +int gp_WriteToString(graphP theGraph, char **pOutputStr, int Mode) +{ + int RetVal; + + if (theGraph == NULL || pOutputStr == NULL) + return NOTOK; + + strOrFileP outputContainer = sf_New(NULL, NULL, WRITETEXT); + if (outputContainer == NULL) + return NOTOK; + + RetVal = _WriteGraph(theGraph, &outputContainer, pOutputStr, Mode); + + // N.B. Since we pass ownership of the outputContainer to the + // G6WriteIterator when we WRITE_G6, we make sure to take the string *before* + // we endG6WriteIteration(), since that calls sf_Free() on the g6Output + // (i.e. outputContainer) and therefore sb_Free() on theStr. This means + // that we need to make sure outputContainer and theStr it contains are + // both non-NULL before trying to take the string, as WRITE_ADJLIST, + // WRITE_ADJMATRIX, and WRITE_DEBUGINFO do *not* clean up the outputContainer. + if (RetVal == OK && outputContainer != NULL) + { + (*pOutputStr) = sf_takeTheStr(outputContainer); + } + + if ((*pOutputStr) == NULL || strlen(*pOutputStr) == 0) + RetVal = NOTOK; + + if (outputContainer != NULL) + sf_Free(&outputContainer); + outputContainer = NULL; + + return RetVal; +} + +/******************************************************************** + _WriteGraph() + Writes theGraph into the strOrFile container. + + Pass WRITE_G6, WRITE_ADJLIST, WRITE_ADJMATRIX, or WRITE_DEBUGINFO for the Mode + + NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST + + Returns NOTOK on error, OK on success. + ********************************************************************/ + +int _WriteGraph(graphP theGraph, strOrFileP *outputContainer, char **pOutputStr, int Mode) +{ + int RetVal = OK; + + switch (Mode) + { + case WRITE_G6: + RetVal = _WriteGraphToG6StrOrFile(theGraph, (*outputContainer), pOutputStr); + // Since G6WriteIterator owns the outputContainer, it'll + // free it, so don't want to try to double-free + (*outputContainer) = NULL; + break; + case WRITE_ADJLIST: + RetVal = _WriteAdjList(theGraph, (*outputContainer)); + break; + case WRITE_ADJMATRIX: + RetVal = _WriteAdjMatrix(theGraph, (*outputContainer)); + break; + case WRITE_DEBUGINFO: + RetVal = _WriteDebugInfo(theGraph, (*outputContainer)); + break; + default: + RetVal = NOTOK; + break; + } + + if (RetVal == OK) + { + char *extraData = NULL; + + RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData); + + if (extraData != NULL) + { + if (sf_fputs(extraData, (*outputContainer)) == EOF) + RetVal = NOTOK; + free(extraData); + } + } + + return RetVal; +} + +/******************************************************************** + _WritePostprocess() + + By default, no additional information is written. + ********************************************************************/ + +int _WritePostprocess(graphP theGraph, char **pExtraData) +{ + return OK; +} + +/******************************************************************** + _Log() + + When the project is compiled with LOGGING enabled, this method writes + a string to the file PLANARITY.LOG in the current working directory. + On first write, the file is created or cleared. + Call this method with NULL to close the log file. + ********************************************************************/ + +void _Log(char *Str) +{ + static FILE *logfile = NULL; + + if (logfile == NULL) + { + if ((logfile = fopen("PLANARITY.LOG", WRITETEXT)) == NULL) + return; + } + + if (Str != NULL) + { + fprintf(logfile, "%s", Str); + fflush(logfile); + } + else + fclose(logfile); +} + +void _LogLine(char *Str) +{ + _Log(Str); + _Log("\n"); +} + +static char LogStr[MAXLINE + 1]; + +char *_MakeLogStr1(char *format, int one) +{ + sprintf(LogStr, format, one); + return LogStr; +} + +char *_MakeLogStr2(char *format, int one, int two) +{ + sprintf(LogStr, format, one, two); + return LogStr; +} + +char *_MakeLogStr3(char *format, int one, int two, int three) +{ + sprintf(LogStr, format, one, two, three); + return LogStr; +} + +char *_MakeLogStr4(char *format, int one, int two, int three, int four) +{ + sprintf(LogStr, format, one, two, three, four); + return LogStr; +} + +char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five) +{ + sprintf(LogStr, format, one, two, three, four, five); + return LogStr; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c new file mode 100644 index 000000000..f617ac7f5 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c @@ -0,0 +1,718 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include +#include +#include +#include + +#include "../lowLevelUtils/appconst.h" +#include "strOrFile.h" + +/******************************************************************** + sf_New() + + Accepts a FILE pointer XOR a string, which are owned by the container. + + Returns the allocated string-or-file container, or NULL on error. + ********************************************************************/ + +strOrFileP sf_New(char *theStr, char *fileName, char *ioMode) +{ + strOrFileP theStrOrFile; + int containerType = 0; + + if ((fileName != NULL) && (theStr != NULL)) + return NULL; + + theStrOrFile = (strOrFileP)calloc(sizeof(strOrFile), 1); + if (theStrOrFile != NULL) + { + if (fileName != NULL) + { + // N.B. If the ioMode is specified but is neither + // READTEXT nor WRITETEXT, error out + if (ioMode != NULL && + strncmp(ioMode, READTEXT, strlen(READTEXT)) != 0 && + strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + FILE *pFile; + + // N.B. If the fileName indicates a stream, then make sure + // ioMode is correct. Since we previously made sure that + // non-NULL ioMode must either refer to READTEXT or WRITETEXT, + // we don't have to worry about freeing up the former string + // before re-assigning. + if (strcmp(fileName, "stdin") == 0) + { + if (ioMode != NULL && strncmp(ioMode, READTEXT, strlen(READTEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + pFile = stdin; + containerType = INPUT_CONTAINER; + } + else if (strcmp(fileName, "stdout") == 0) + { + if (ioMode != NULL && strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + pFile = stdout; + containerType = OUTPUT_CONTAINER; + } + else if (strcmp(fileName, "stderr") == 0) + { + if (ioMode != NULL && strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) != 0) + { + sf_Free(&theStrOrFile); + return NULL; + } + + pFile = stderr; + containerType = OUTPUT_CONTAINER; + } + else + { + // N.B. Clean up and return NULL if: + // - fileName is not one of stdin/stdout/stderr, and + // if the ioMode is not given + // - ioMode is given but is neither READTEXT nor + // WRITETEXT + if (ioMode == NULL || + (pFile = fopen(fileName, ioMode)) == NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + + if (strncmp(ioMode, READTEXT, strlen(READTEXT)) == 0) + containerType = INPUT_CONTAINER; + else if (strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) == 0) + containerType = OUTPUT_CONTAINER; + } + + theStrOrFile->pFile = pFile; + } + else + { + if (strncmp(ioMode, READTEXT, strlen(READTEXT)) == 0) + containerType = INPUT_CONTAINER; + else if (strncmp(ioMode, WRITETEXT, strlen(WRITETEXT)) == 0) + containerType = OUTPUT_CONTAINER; + + if (containerType != INPUT_CONTAINER && theStr != NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + + strBufP strBufToAssign = sb_New(0); + if (strBufToAssign == NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + + if (theStr != NULL && sb_ConcatString(strBufToAssign, theStr) != OK) + { + sb_Free(&strBufToAssign); + sf_Free(&theStrOrFile); + return NULL; + } + + theStrOrFile->theStr = strBufToAssign; + } + + theStrOrFile->containerType = containerType; + + theStrOrFile->ungetBuf = sp_New(MAXLINE); + if (theStrOrFile->ungetBuf == NULL) + { + sf_Free(&theStrOrFile); + return NULL; + } + } + + return theStrOrFile; +} + +/******************************************************************** + sf_ValidateStrOrFile() + + Ensures that theStrOrFile: + 1. Is not NULL + 2. Has ungetBuf allocated + 3. Both pFile and theStr are not NULL + 4. Both pFile and theStr are not both assigned (since this container + should only contain one source). + 5. containerType is either set to INPUT_CONTAINER or OUTPUT_CONTAINER + + Returns NOTOK if any of these conditions are not met, otherwise OK. + ********************************************************************/ + +int sf_ValidateStrOrFile(strOrFileP theStrOrFile) +{ + if (theStrOrFile == NULL || + theStrOrFile->ungetBuf == NULL || + (theStrOrFile->pFile == NULL && theStrOrFile->theStr == NULL) || + (theStrOrFile->pFile != NULL && theStrOrFile->theStr != NULL) || + (theStrOrFile->containerType != INPUT_CONTAINER && + theStrOrFile->containerType != OUTPUT_CONTAINER)) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_getc() + + If strOrFileP has a non-empty ungetBuf, pop and return the character. + If the ungetBuf is empty, then we'll read from pFile using getc() OR + from theStr by fetching the character at theStrPos and incrementing + theStrPos. + ********************************************************************/ + +char sf_getc(strOrFileP theStrOrFile) +{ + char theChar = EOF; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return EOF; + + if ((theStrOrFile->ungetBuf != NULL) && (sp_GetCurrentSize(theStrOrFile->ungetBuf) > 0)) + { + int currChar = 0; + sp_Pop(theStrOrFile->ungetBuf, currChar); + theChar = (char)currChar; + } + else if (theStrOrFile->pFile != NULL) + theChar = (char)getc(theStrOrFile->pFile); + else if (theStrOrFile->theStr != NULL && sb_GetUnreadCharCount(theStrOrFile->theStr) > 0) + { + theChar = sb_GetReadString(theStrOrFile->theStr)[0]; + if (theChar != EOF) + sb_ReadSkipChar(theStrOrFile->theStr); + } + + return theChar; +} + +/******************************************************************** + sf_ReadSkipChar() + + Calls sf_getc() and does nothing with the returned character + ********************************************************************/ + +int sf_ReadSkipChar(strOrFileP theStrOrFile) +{ + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + if (sf_getc(theStrOrFile) == EOF) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ReadSkipWhitespace() + + Repeatedly calls sf_getc() to find the next non-space character + before hitting EOF + ********************************************************************/ + +int sf_ReadSkipWhitespace(strOrFileP theStrOrFile) +{ + char currChar = EOF; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + while ((currChar = sf_getc(theStrOrFile)) != EOF && isspace(currChar)) + { + continue; + } + + if (sf_ungetc(currChar, theStrOrFile) != currChar) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ReadSingleDigit() + + Calls sf_getc() and tests whether the character read corresponds to + a digit. + + Returns NOTOK if the character returned from sf_getc() is not a digit. + Assigns the digit read to the int * and returns OK upon success. + ********************************************************************/ + +int sf_ReadSingleDigit(int *digitToRead, strOrFileP theStrOrFile) +{ + int candidateDigit = EOF; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + candidateDigit = sf_getc(theStrOrFile); + if (!isdigit(candidateDigit)) + return NOTOK; + + // N.B. Subtract '0' = 48 to convert the digit + // char to the corresponding int + (*digitToRead) = candidateDigit - '0'; + return OK; +} + +/******************************************************************** + sf_ReadInteger() + + Repeatedly calls sf_getc() to obtain the characters corresponding to + an int, then parses that char* using sscanf() to extract the integer. + + Returns OK if successfully extracted the digits of and produced the + int from theStrOrFile, or NOTOK otherwise. + ********************************************************************/ + +int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile) +{ + int exitCode = OK; + + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NOTOK; + + char intCandidateStr[MAXCHARSFOR32BITINT + 1]; + memset(intCandidateStr, '\0', (MAXCHARSFOR32BITINT + 1) * sizeof(char)); + + int intCandidate = 0, intCandidateIndex = 0; + char currChar = '\0', nextChar = '\0'; + bool startedReadingInt = FALSE, isNegative = FALSE; + do + { + currChar = sf_getc(theStrOrFile); + if (currChar == '-') + { + if (startedReadingInt) + { + exitCode = NOTOK; + break; + } + else + { + nextChar = sf_getc(theStrOrFile); + if (sf_ungetc(nextChar, theStrOrFile) != nextChar) + { + exitCode = NOTOK; + break; + } + + if (!isdigit(nextChar)) + { + exitCode = NOTOK; + break; + } + else + { + intCandidateStr[intCandidateIndex++] = currChar; + isNegative = TRUE; + } + } + } + else if (isdigit(currChar)) + { + intCandidateStr[intCandidateIndex++] = currChar; + startedReadingInt = TRUE; + } + else + { + if (sf_ungetc(currChar, theStrOrFile) != currChar) + exitCode = NOTOK; + break; + } + + if ( + (!isNegative && (intCandidateIndex == (MAXCHARSFOR32BITINT - 2))) || + (isNegative && (intCandidateIndex == (MAXCHARSFOR32BITINT - 1)))) + { + if (sscanf(intCandidateStr, "%d", &intCandidate) != 1) + exitCode = NOTOK; + else + { + nextChar = sf_getc(theStrOrFile); + if (isdigit(nextChar)) + { + // N.B. The only way we could be here is if `i` is less than the max possible + // length of the string representation of a signed 32-bit integer (10 for + // positive and 11 for negative) + int underflowThreshold = (INT32_MIN / 10), overflowThreshold = (INT32_MAX / 10); + if (isNegative) + { + if (intCandidate < underflowThreshold) + exitCode = NOTOK; + else if (intCandidate == underflowThreshold && nextChar == '9') + exitCode = NOTOK; + } + else + { + if (intCandidate > overflowThreshold) + exitCode = NOTOK; + else if (intCandidate == overflowThreshold && (nextChar == '8' || nextChar == '9')) + exitCode = NOTOK; + } + + if (exitCode == OK) + { + intCandidateStr[intCandidateIndex++] = nextChar; + } + } + else if (sf_ungetc(nextChar, theStrOrFile) != nextChar) + exitCode = NOTOK; + } + break; + } + } while (currChar != EOF); + + if (exitCode == OK) + { + if (sscanf(intCandidateStr, "%d", &intCandidate) != 1) + exitCode = NOTOK; + else + (*intToRead) = intCandidate; + } + + return exitCode; +} + +/******************************************************************** + sf_ReadSkipInteger() + + Calls sf_ReadInteger() and discards the result. + ********************************************************************/ + +int sf_ReadSkipInteger(strOrFileP theStrOrFile) +{ + int temp = 0; + + if (sf_ReadInteger(&temp, theStrOrFile) != OK) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ReadSkipLineRemainder() + + Calls sf_fgets() and discards the result. + ********************************************************************/ + +int sf_ReadSkipLineRemainder(strOrFileP theStrOrFile) +{ + char lineRemainderToSkip[MAXLINE + 1]; + memset(lineRemainderToSkip, '\0', (MAXLINE + 1)); + + if (sf_fgets(lineRemainderToSkip, MAXLINE, theStrOrFile) == NULL) + return NOTOK; + + return OK; +} + +/******************************************************************** + sf_ungetc() + + Order of parameters matches stdio ungetc(). + + For both the case where the strOrFile contains a FILE * and the case + where it contains a strBufP, we unget to the ungetBuf; this ungetBuf + is consumed first when we sf_getc(), sf_fgets(), etc. + + Like ungetc() in stdio, on success theChar is returned. On failure, + EOF is returned. + ********************************************************************/ + +char sf_ungetc(char theChar, strOrFileP theStrOrFile) +{ + if (theChar == EOF || + sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER || + sp_GetCurrentSize(theStrOrFile->ungetBuf) >= sp_GetCapacity(theStrOrFile->ungetBuf)) + return EOF; + + sp_Push(theStrOrFile->ungetBuf, theChar); + return theChar; +} + +/******************************************************************** + sf_ungets() + + Pushes characters of strToUnget in reverse order to the ungetBuf so + that they can be fetched from the ungetBuf in the order of the + original string. + + Returns OK on success and NOTOK on failure. + ********************************************************************/ + +int sf_ungets(char *strToUnget, strOrFileP theStrOrFile) +{ + if (sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER || + (int)strlen(strToUnget) > (sp_GetCapacity(theStrOrFile->ungetBuf) - sp_GetCurrentSize(theStrOrFile->ungetBuf))) + return NOTOK; + + for (int i = (strlen(strToUnget) - 1); i >= 0; i--) + sp_Push(theStrOrFile->ungetBuf, strToUnget[i]); + + return OK; +} + +/******************************************************************** + sf_fgets() + + Order of parameters matches stdio fgets(). + + First param is the string to populate (assumes allocated (count + 1) + bytes), second param is the max number of characters to read, and + third param is the pointer to the string-or-file container from which + we wish to read count characters (or up to and including \n). + + Like fgets() in stdio, this function doesn't check that enough memory + is allocated for str to contain count characters plus \0. + + Like fgets() in stdio, on success the pointer to the buffer is returned. + On failure, NULL is returned. + ********************************************************************/ + +char *sf_fgets(char *str, int count, strOrFileP theStrOrFile) +{ + if (str == NULL || count < 0 || + sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != INPUT_CONTAINER) + return NULL; + + int charsToReadFromUngetBuf = 0; + int charsToReadFromStrOrFile = count; + if (theStrOrFile->ungetBuf != NULL) + { + int numCharsInUngetBuf = sp_GetCurrentSize(theStrOrFile->ungetBuf); + if (numCharsInUngetBuf > 0) + { + charsToReadFromUngetBuf = (count > numCharsInUngetBuf) ? numCharsInUngetBuf : count; + char currChar = '\0'; + bool encounteredNewline = FALSE; + for (int i = 0; i < charsToReadFromUngetBuf; i++) + { + currChar = sf_getc(theStrOrFile); + if (currChar == EOF) + return NULL; + str[i] = currChar; + str[i + 1] = '\0'; + // N.B. fgets() includes the \n in the string returned, and + // no further characters shall be read + if (currChar == '\n') + { + encounteredNewline = TRUE; + break; + } + } + // N.B. If we broke out of the loop early due to \n, do not read + // any further characters from stream + charsToReadFromStrOrFile = (encounteredNewline) ? 0 : ((count > numCharsInUngetBuf) ? (count - charsToReadFromUngetBuf) : 0); + } + } + + if (charsToReadFromStrOrFile > 0) + { + if (theStrOrFile->pFile != NULL) + { + // N.B. if fgets() returns NULL (can't read more characters) AND the ungetBuf was empty, + // then return NULL (error trying to read from empty stream). Otherwise, return str (that + // was read from ungetBuf) + if (fgets(str + charsToReadFromUngetBuf, charsToReadFromStrOrFile, theStrOrFile->pFile) == NULL) + { + if (charsToReadFromUngetBuf == 0) + return NULL; + } + } + else if (theStrOrFile->theStr != NULL) + { + char *theStrBuf = sb_GetReadString(theStrOrFile->theStr); + if (theStrBuf != NULL && sb_GetUnreadCharCount(theStrOrFile->theStr) > 0) + { + if (strncpy( + str + charsToReadFromUngetBuf, + theStrBuf, + charsToReadFromStrOrFile) == NULL) + return NULL; + + sb_SetReadPos(theStrOrFile->theStr, (sb_GetReadPos(theStrOrFile->theStr) + charsToReadFromStrOrFile)); + } + else if (charsToReadFromUngetBuf == 0) + return NULL; + } + } + + return str; +} + +/******************************************************************** + sf_fputs() + + Order of parameters matches stdio fputs(). + + First param is the string to append, and the second param is the + string-or-file container to which we wish to append. + + On success, returns the number of characters written. + On failure, returns EOF. + ********************************************************************/ + +int sf_fputs(char *strToWrite, strOrFileP theStrOrFile) +{ + int outputLen = EOF; + + if (strToWrite == NULL || + sf_ValidateStrOrFile(theStrOrFile) != OK || + theStrOrFile->containerType != OUTPUT_CONTAINER) + return EOF; + + // N.B. fputs() will fail and return EOF if pFile doesn't correspond + // to an output stream + if (theStrOrFile->pFile != NULL) + outputLen = fputs(strToWrite, theStrOrFile->pFile); + else if (theStrOrFile->theStr != NULL) + { + if (sb_ConcatString(theStrOrFile->theStr, strToWrite) == OK) + outputLen = strlen(strToWrite); + else + outputLen = EOF; + } + + return outputLen; +} + +/******************************************************************** + sf_takeTheStr() + + Returns the char * stored in the string-or-file container and NULLs + out the internal reference so ownership of the memory is transferred + to the caller. + + The pointer returned will be NULL if the strOrFile contains a FILE *. + ********************************************************************/ + +char *sf_takeTheStr(strOrFileP theStrOrFile) +{ + char *theStr = NULL; + if (theStrOrFile->theStr != NULL) + { + theStr = sb_TakeString(theStrOrFile->theStr); + sb_Free((&theStrOrFile->theStr)); + theStrOrFile->theStr = NULL; + } + + return theStr; +} + +/******************************************************************** + sf_closeFile() + + If the strOrFile container contains a string, degenerately returns OK. + + If the strOrFile container contains a FILE pointer: + - if the FILE pointer is one of stdin, stdout, or stderr, calls + fflush() on the stream and captures the errorCode + - else, closes pFile and sets the internal pointer to NULL, then + captures the errorCode from fclose() + If the errorCode is less than 0, returns NOTOK, otherwise returns OK. + ********************************************************************/ + +int sf_closeFile(strOrFileP theStrOrFile) +{ + FILE *pFile = theStrOrFile->pFile; + theStrOrFile->pFile = NULL; + if (pFile != NULL) + { + int errorCode = 0; + + if (pFile == stdin || pFile == stdout || pFile == stderr) + errorCode = fflush(pFile); + else + errorCode = fclose(pFile); + + if (errorCode < 0) + return NOTOK; + } + + if (theStrOrFile->ungetBuf != NULL) + { + sp_Free(&(theStrOrFile->ungetBuf)); + } + theStrOrFile->ungetBuf = NULL; + + return OK; +} + +/******************************************************************** + sf_Free() + + Receives a pointer-pointer to a string-or-file container. + + If the strOrFile contains a string which has not yet been "taken" + using sf_takeTheStr() (i.e. we want inputStr to be freed, and in an + error state we want to free outputStr), the string is freed, the + internal pointer is set to NULL, and theStrPos is set to 0. + + If the strOrFile contains a FILE pointer, we call sf_closeFile() + and set the internal pointer to NULL. Note that unless we are in an + error state when sf_Free() is called, sf_closeFile should have already + been called. + + Finally, we use the indirection operator to free the strOrFile + container and set the pointer to NULL. + ********************************************************************/ + +void sf_Free(strOrFileP *pStrOrFile) +{ + if (pStrOrFile != NULL && (*pStrOrFile) != NULL) + { + if ((*pStrOrFile)->theStr != NULL) + sb_Free((&(*pStrOrFile)->theStr)); + (*pStrOrFile)->theStr = NULL; + + // TODO: (#56) if the strOrFile container's FILE pointer + // corresponds to an output file, i.e. ioMode is 'w', + // we should try to remove the file since the error state + // means the contents are invalid + if ((*pStrOrFile)->pFile != NULL) + sf_closeFile((*pStrOrFile)); + (*pStrOrFile)->pFile = NULL; + + if ((*pStrOrFile)->ungetBuf != NULL) + { + sp_Free(&((*pStrOrFile)->ungetBuf)); + } + (*pStrOrFile)->ungetBuf = NULL; + + free(*pStrOrFile); + (*pStrOrFile) = NULL; + } +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h new file mode 100644 index 000000000..8a2252386 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h @@ -0,0 +1,61 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef STR_OR_FILE_H +#define STR_OR_FILE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include "../lowLevelUtils/stack.h" +#include "strbuf.h" + +#define INPUT_CONTAINER 1 +#define OUTPUT_CONTAINER 2 + typedef struct + { + strBufP theStr; + FILE *pFile; + int containerType; + stackP ungetBuf; + + } strOrFile; + + typedef strOrFile *strOrFileP; + + strOrFileP sf_New(char *theStr, char *fileName, char *ioMode); + int sf_ValidateStrOrFile(strOrFileP theStrOrFile); + + char sf_getc(strOrFileP theStrOrFile); + int sf_ReadSkipChar(strOrFileP theStrOrFile); + int sf_ReadSkipWhitespace(strOrFileP theStrOrFile); + int sf_ReadSingleDigit(int *digitToRead, strOrFileP theStrOrFile); + int sf_ReadInteger(int *intToRead, strOrFileP theStrOrFile); + int sf_ReadSkipInteger(strOrFileP theStrOrFile); + int sf_ReadSkipLineRemainder(strOrFileP theStrOrFile); + + char sf_ungetc(char theChar, strOrFileP theStrOrFile); + int sf_ungets(char *contentsToUnget, strOrFileP theStrOrFile); + + char *sf_fgets(char *str, int count, strOrFileP theStrOrFile); + + int sf_fputs(char *strToWrite, strOrFileP theStrOrFile); + + char *sf_takeTheStr(strOrFileP theStrOrFile); + + int sf_closeFile(strOrFileP theStrOrFile); + + void sf_Free(strOrFileP *pStrOrFile); + +#ifdef __cplusplus +} +#endif + +#endif /* STR_OR_FILE_H */ diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c new file mode 100644 index 000000000..eea9e50b2 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c @@ -0,0 +1,247 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../lowLevelUtils/appconst.h" +#include "strbuf.h" +#include +#include + +/******************************************************************** + sb_New() + Allocates a string buffer with space for capacity characters, plus one + for a null terminator + + Returns the allocated string buffer, or NULL on error. + ********************************************************************/ +strBufP sb_New(int capacity) +{ + strBufP theStrBuf; + + if (capacity < 0) + return NULL; + + theStrBuf = (strBufP)malloc(sizeof(strBuf)); + + if (theStrBuf != NULL) + { + theStrBuf->buf = (char *)malloc((capacity + 1) * sizeof(char)); + if (theStrBuf->buf == NULL) + { + free(theStrBuf); + theStrBuf = NULL; + } + } + + if (theStrBuf != NULL) + { + theStrBuf->capacity = capacity; + sb_ClearBuf(theStrBuf); + } + + return theStrBuf; +} + +/******************************************************************** + sb_Free() + Receives a pointer-pointer to a string buffer. + Frees the memory of the string buffer structure and the string buffer + it contains. Using the pointer-pointer, sets the pointer to NULL. + ********************************************************************/ +void sb_Free(strBufP *pStrBuf) +{ + if (pStrBuf != NULL && *pStrBuf != NULL) + { + (*pStrBuf)->capacity = (*pStrBuf)->size = (*pStrBuf)->readPos = 0; + + if ((*pStrBuf)->buf != NULL) + free((*pStrBuf)->buf); + (*pStrBuf)->buf = NULL; + + free(*pStrBuf); + *pStrBuf = NULL; + } +} + +/******************************************************************** + sb_CleareBuf() + The string buffer is changed, if not NULL, to hold an empty string, + including setting the size and position to 0. The capacity is not + changed. + ********************************************************************/ +void sb_ClearBuf(strBufP theStrBuf) +{ + if (theStrBuf != NULL) + { + if (theStrBuf->buf != NULL) + theStrBuf->buf[0] = '\0'; + theStrBuf->size = theStrBuf->readPos = 0; + } +} + +/******************************************************************** + sb_Copy() + Receives strBuf pointers for a destination and source. + Ensures that the destination has the capacity of the source. + Copies the source string content into the destination, and sets the + size correctly. + Returns OK for success, NOTOK on param or memory allocation error. + ********************************************************************/ +int sb_Copy(strBufP strBufDst, strBufP strBufSrc) +{ + char *tempBuf; + strBufP newStrBuf = sb_Duplicate(strBufSrc); + + if (strBufDst == NULL || strBufSrc == NULL || newStrBuf == NULL) + { + sb_Free(&newStrBuf); + return NOTOK; + } + + tempBuf = strBufDst->buf; + strBufDst->buf = newStrBuf->buf; + newStrBuf->buf = tempBuf; + + sb_Free(&newStrBuf); + + strBufDst->size = strBufSrc->size; + strBufDst->capacity = strBufSrc->capacity; + + return OK; +} + +/******************************************************************** + sb_Duplicate() + Receives a strBuf pointer. Allocates a new strBuf structure with the + same capacity, and initially clears it. If the received strBuf has + a non-empty string, then it is copied into the duplicate, and + the duplicates size is set correctly. + Returns the duplicate, or NULL on memory allocation error. + ********************************************************************/ +strBufP sb_Duplicate(strBufP theStrBuf) +{ + strBufP newStrBuf = sb_New(theStrBuf->capacity); + + if (newStrBuf == NULL) + return NULL; + + if (theStrBuf->size > 0) + { + strcpy(newStrBuf->buf, theStrBuf->buf); + newStrBuf->size = theStrBuf->size; + } + + return newStrBuf; +} + +/******************************************************************** + sb_ReadSkipWhitespace() + Advances the read position managed by the string buffer by skipping + any number of whitespace characters, if present. + ********************************************************************/ + +void sb_ReadSkipWhitespace(strBufP theStrBuf) +{ + if (theStrBuf != NULL && theStrBuf->buf != NULL) + { + while (isspace(theStrBuf->buf[theStrBuf->readPos])) + theStrBuf->readPos++; + } +} + +/******************************************************************** + sb_ReadSkipInteger() + Advances the read position managed by the string buffer by skipping + an initial negative sign, if present, then skipping any number of + numeric digits, if present. + ********************************************************************/ +void sb_ReadSkipInteger(strBufP theStrBuf) +{ + if (theStrBuf != NULL && theStrBuf->buf != NULL) + { + if (theStrBuf->buf[theStrBuf->readPos] == '-') + theStrBuf->readPos++; + + while (isdigit(theStrBuf->buf[theStrBuf->readPos])) + theStrBuf->readPos++; + } +} + +/******************************************************************** + sb_ConcatString() + Appends the content of string s to the end of the content already + in the string buffer. If the append would exceed the capacity of + the buffer, then the buffer capacity is first increased. It is + increased to one greater than the sum of the current capacity and the + new string size or double the capacity (to ensure the memory space is + big enough for both strings and linear time performance over many + small concatenations). + Returns OK on success, NOTOK on error + ********************************************************************/ +int sb_ConcatString(strBufP theStrBuf, char *s) +{ + int slen = s == NULL ? 0 : strlen(s); + + if (slen == 0) + return OK; + + if (theStrBuf == NULL || theStrBuf->buf == NULL) + return NOTOK; + + if (theStrBuf->size + slen > theStrBuf->capacity) + { + int newLen = theStrBuf->size + slen > 2 * theStrBuf->capacity ? theStrBuf->size + slen : 2 * theStrBuf->capacity; + char *newBuf = (char *)malloc((newLen + 1) * sizeof(char)); + + if (newBuf == NULL) + return NOTOK; + + strcpy(newBuf, theStrBuf->buf); + free(theStrBuf->buf); + theStrBuf->buf = newBuf; + theStrBuf->capacity = newLen; + } + + strcpy(theStrBuf->buf + theStrBuf->size, s); + theStrBuf->size += slen; + + return OK; +} + +/******************************************************************** + sb_ConcatChar() + Converts ch into a one-character string containing ch, then invokes + sb_ConcatStr(). + Returns Same as sb_ConcatStr() + ********************************************************************/ +int sb_ConcatChar(strBufP theStrBuf, char ch) +{ + char s[2]; + s[0] = ch; + s[1] = '\0'; + return sb_ConcatString(theStrBuf, s); +} + +/******************************************************************** + sb_TakeString() + Extracts the buffer string from the received string buffer and + returns it. The string buffer is changed to contain a zero length + string, so that the buffer can be reused or freed with sb_Free(). + Returns NULL on error or a string to be freed by the caller on success. + ********************************************************************/ +char *sb_TakeString(strBufP theStrBuf) +{ + char *theBuf = NULL; + + if (theStrBuf == NULL) + return NULL; + + theBuf = theStrBuf->buf; + theStrBuf->buf = (char *)malloc(sizeof(char)); + theStrBuf->buf[0] = '\0'; + theStrBuf->size = theStrBuf->capacity = 0; + + return theBuf; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h new file mode 100644 index 000000000..28783222c --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef STRBUF_H +#define STRBUF_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +// includes mem functions like memcpy +#include + + typedef struct + { + char *buf; + int size, capacity, readPos; + } strBuf; + + typedef strBuf *strBufP; + + strBufP sb_New(int); + void sb_Free(strBufP *); + + void sb_ClearBuf(strBufP); + int sb_Copy(strBufP, strBufP); + strBufP sb_Duplicate(strBufP); + +#define sb_GetFullString(theStrBuf) (theStrBuf->buf) +#define sb_GetSize(theStrBuf) (theStrBuf->size) +#define sb_GetCapacity(theStrBuf) (theStrBuf->capacity) +#define sb_GetUnreadCharCount(theStrBuf) (theStrBuf->size - theStrBuf->readPos) +#define sb_GetReadString(theStrBuf) ((theStrBuf != NULL && theStrBuf->buf != NULL) ? (theStrBuf->buf + theStrBuf->readPos) : NULL) + +#define sb_GetReadPos(theStrBuf) (theStrBuf->readPos) +#define sb_SetReadPos(theStrBuf, theReadPos) \ + { \ + theStrBuf->readPos = theReadPos; \ + } + + void sb_ReadSkipWhitespace(strBufP); + void sb_ReadSkipInteger(strBufP); +#define sb_ReadSkipChar(theStrBuf) \ + { \ + theStrBuf->readPos++; \ + } + + int sb_ConcatString(strBufP, char *); + int sb_ConcatChar(strBufP, char); + + char *sb_TakeString(strBufP); + +#ifndef SPEED_MACROS +// Optimized SPEED_MACROS versions of larger methods are not used in this module +#else +// Optimized SPEED_MACROS versions of larger methods are not used in this module +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* STRBUF_H */ diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c new file mode 100644 index 000000000..8902c6876 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c @@ -0,0 +1,73 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include +#include +#include +#include + +#include "apiutils.h" +#include "appconst.h" + +int quietMode = FALSE; + +int getQuietModeSetting(void) +{ + return quietMode; +} + +void setQuietModeSetting(int newQuietModeSetting) +{ + quietMode = newQuietModeSetting; +} + +void Message(char *message) +{ + if (!getQuietModeSetting()) + { + fprintf(stdout, "%s", message); + fflush(stdout); + } +} + +void ErrorMessage(char *message) +{ + if (!getQuietModeSetting()) + { + fprintf(stderr, "%s", message); + fflush(stderr); + } +} + +int GetNumCharsToReprInt(int theNum, int *numCharsRequired) +{ + if (numCharsRequired == NULL) + return NOTOK; + + int charCount = 0; + + if (theNum < 0) + { + charCount++; + // N.B. since 32-bit signed integers are represented using twos-complement, + // the absolute value of INT_MIN is not defined; however, adding 1 to this + // min value before taking the absolute value will still require the same + // number of digits. + if ((theNum == INT_MIN) || (theNum == INT8_MAX) || (theNum == INT16_MIN) || (theNum == INT32_MIN)) + theNum++; + theNum = abs(theNum); + } + + while (theNum > 0) + { + theNum /= 10; + charCount++; + } + + (*numCharsRequired) = charCount; + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h new file mode 100644 index 000000000..7ac8ae394 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ +#ifndef APIUTILS_H +#define APIUTILS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "stdio.h" + +// N.B. Every time this is used to create a string for a message or +// error message, the developer must check that there will not be a +// memory overwrite error. +#define MAXLINE 1024 + +// N.B. Every time you're trying to read a 32-bit int from a string, +// you should only need to read this many characters: an optional '-', +// followed by 10 digits (max signed 32-bit int value is 2,147,483,647). +// One must always allocate an additional byte for the null-terminator! +#define MAXCHARSFOR32BITINT 11 + + extern int quietMode; + + extern int getQuietModeSetting(void); + extern void setQuietModeSetting(int); + + extern void Message(char *message); + extern void ErrorMessage(char *message); + + int GetNumCharsToReprInt(int theNum, int *numCharsRequired); +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/appconst.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/appconst.h new file mode 100644 index 000000000..d813c9b9b --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/appconst.h @@ -0,0 +1,103 @@ +#ifndef APPCONST_H +#define APPCONST_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "apiutils.h" + +// NOTE: This is defined on 32- and 64-bit Windows systems; see +// https://sourceforge.net/p/predef/wiki/OperatingSystems +#if defined(WIN32) || defined(_WIN32) +#define WINDOWS +#endif + +/* Defines fopen strings for reading and writing text files on PC and UNIX */ + +#ifdef WINDOWS +#define READTEXT "rt" +#define WRITETEXT "wt" +#define FILE_DELIMITER '\\' +#else +#define READTEXT "r" +#define WRITETEXT "w" +#define FILE_DELIMITER '/' +#endif + +// When PROFILE is defined, prints out run-time stats on a number of subordinate +// routines in the embedder + +// #define PROFILE +#ifdef PROFILE +#include "platformTime.h" +#endif + +/* Define DEBUG to get additional debugging. The default is to define it when MSC does */ + +#ifdef _DEBUG +#define DEBUG +#endif + +/* Some low-level functions are replaced by faster macros, except when debugging */ + +#define SPEED_MACROS +#ifdef DEBUG +#undef SPEED_MACROS +#endif + +/* Return status values; OK/NOTOK behave like Boolean true/false, + not like program exit codes. */ + +#define OK 1 +#define NOTOK 0 + +#ifdef DEBUG +#undef NOTOK +extern int debugNOTOK(void); +#include +#define NOTOK (printf("NOTOK on Line %d of %s\n", __LINE__, __FILE__), debugNOTOK()) +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL 0L +#endif + +/* Array indices are used as pointers, and NIL means bad pointer */ +#define USE_FASTER_1BASEDARRAYS + +#ifdef USE_0BASEDARRAYS +#undef USE_FASTER_1BASEDARRAYS +#endif + +#ifdef USE_FASTER_1BASEDARRAYS +// This definition is used with 1-based array indexing +#define NIL 0 +#define NIL_CHAR 0x00 +#else +// This definition is used with 0-based array indexing +#define NIL -1 +#define NIL_CHAR 0xFF +#endif + +/******************************************************************** + A few simple integer selection macros + ********************************************************************/ + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +#define MIN3(x, y, z) MIN(MIN((x), (y)), MIN((y), (z))) +#define MAX3(x, y, z) MAX(MAX((x), (y)), MAX((y), (z))) + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.c new file mode 100644 index 000000000..99f1c34a2 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.c @@ -0,0 +1,278 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define _LISTCOLL_C + +#include "appconst.h" +#include "listcoll.h" +#include + +/***************************************************************************** + The data structure defined by this module manages a set of N objects + arranged as a collection of circular lists, each containing distinct + elements from the set. + + On construction, LCNew() creates an array of N nodes, each containing a + prev and next pointer. The identity of the node is given by its array index. + Each node's prev and next pointers are set to NIL, indicating that the node + is not currently part of a list. LCReset() can be called to reset all + pointers to NIL. + + The function LCFree() deallocates the collection of lists and clears the + pointer variable used to pass the collection. + + An empty list is indicated by NIL. To begin a list with node I, call + LCPrepend() or LCAppend() with the NIL list and with I as the node. The prev + and next pointers in node I are set to I and I is returned as the head of + the list. + + Future calls to LCPrepend() add a node J as the new first element of the list, + so the list given as input is pointed to by J's next, and J is returned as + the head of the list. + + Future calls to LCAppend() add a node J as the new last element, so the prev + pointer of the list given as input will indicate node J, and the input list + is returned as the head of the list. + + LCInsertAfter() adds a node immediately after a given anchor node. + + LCInsertBefore() adds a node immediately before a given anchor node and has + the same effect on a list as LCPrepend(). + + The function LCDelete() removes a node I from a list L. If node I is in the + list alone, then its pointers are set to NIL, and NIL is returned as the list. + If node I is not alone in the list, but it is the head of the list (in other + words, I is equal to L), then L's sucessor is returned as the new head of the + list. Whether or not I equals L, node I is deleted by joining its predecessor + and successor nodes. + + LCCopy() copies the contents of one collection to another if both are of + equal size. + + LCGetNext() is used for forward iteration through a list in the collection. + The expected iteration pattern is first to process the node one has, then call + LCGetNext() to get the next node, so if the result of LCGetNext() would be the + head of the list, then NIL is returned instead. This simplifies most + coding operations involving LCGetNext(). + + LCGetPrev() is used for backward iteration through a list in the collection. + The expected iteration pattern is that the last list element will be obtained + by an initial call to LCGetPrev() with theNode equal to NIL. This call + should appear outside of the iteration loop. The iteration loop then + proceeds while the current node is not NIL. The loop body processes the + current node, then LCGetPrev() is called with theNode equal to the current + node. LCGetPrev() returns NIL if theNode is equal to theList. Otherwise, + the predecessor of theNode is returned. + + *****************************************************************************/ + +/***************************************************************************** + LCNew() + *****************************************************************************/ + +listCollectionP LCNew(int N) +{ + listCollectionP theListColl = NULL; + + if (N <= 0) + return theListColl; + + theListColl = (listCollectionP)malloc(sizeof(listCollectionRec)); + if (theListColl != NULL) + { + theListColl->List = (lcnode *)malloc(N * sizeof(lcnode)); + if (theListColl->List == NULL) + { + free(theListColl); + theListColl = NULL; + } + else + { + theListColl->N = N; + LCReset(theListColl); + } + } + return theListColl; +} + +/***************************************************************************** + LCFree() + *****************************************************************************/ + +void LCFree(listCollectionP *pListColl) +{ + if (pListColl == NULL || *pListColl == NULL) + return; + + if ((*pListColl)->List != NULL) + free((*pListColl)->List); + + free(*pListColl); + *pListColl = NULL; +} + +/***************************************************************************** + LCInsertAfter() + *****************************************************************************/ + +void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode) +{ + listColl->List[theNewNode].prev = theAnchor; + listColl->List[theNewNode].next = listColl->List[theAnchor].next; + listColl->List[listColl->List[theAnchor].next].prev = theNewNode; + listColl->List[theAnchor].next = theNewNode; +} + +/***************************************************************************** + LCInsertBefore() + *****************************************************************************/ + +void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode) +{ + LCPrepend(listColl, theAnchor, theNewNode); +} + +#ifndef SPEED_MACROS + +/***************************************************************************** + LCReset() + *****************************************************************************/ + +void LCReset(listCollectionP listColl) +{ + int K; + + for (K = 0; K < listColl->N; K++) + listColl->List[K].prev = listColl->List[K].next = NIL; +} + +/***************************************************************************** + LCCopy() + *****************************************************************************/ + +void LCCopy(listCollectionP dst, listCollectionP src) +{ + int K; + + if (dst == NULL || src == NULL || dst->N != src->N) + return; + + for (K = 0; K < dst->N; K++) + dst->List[K] = src->List[K]; +} + +/***************************************************************************** + LCGetNext() + *****************************************************************************/ + +int LCGetNext(listCollectionP listColl, int theList, int theNode) +{ + int next; + + if (listColl == NULL || theList == NIL || theNode == NIL) + return NIL; + next = listColl->List[theNode].next; + return next == theList ? NIL : next; +} + +/***************************************************************************** + LCGetPrev() + *****************************************************************************/ + +int LCGetPrev(listCollectionP listColl, int theList, int theNode) +{ + if (listColl == NULL || theList == NIL) + return NIL; + if (theNode == NIL) + return listColl->List[theList].prev; + if (theNode == theList) + return NIL; + return listColl->List[theNode].prev; +} + +/***************************************************************************** + LCPrepend() + *****************************************************************************/ + +int LCPrepend(listCollectionP listColl, int theList, int theNode) +{ + /* If the append worked, then theNode is last, which in a circular + list is the direct predecessor of the list head node, so we + just back up one. For singletons, the result is unchanged. */ + + return listColl->List[LCAppend(listColl, theList, theNode)].prev; +} + +/***************************************************************************** + LCAppend() + *****************************************************************************/ + +int LCAppend(listCollectionP listColl, int theList, int theNode) +{ + /* If the given list is empty, then the given node becomes the + singleton list output */ + + if (theList == NIL) + { + listColl->List[theNode].prev = listColl->List[theNode].next = theNode; + theList = theNode; + } + + /* Otherwise, make theNode the predecessor of head node of theList, + which is where the last node goes in a circular list. */ + + else + { + int pred = listColl->List[theList].prev; + + listColl->List[theList].prev = theNode; + listColl->List[theNode].next = theList; + listColl->List[theNode].prev = pred; + listColl->List[pred].next = theNode; + } + + /* Return the list (only really important if it was NIL) */ + + return theList; +} + +/***************************************************************************** + LCDelete() + *****************************************************************************/ + +int LCDelete(listCollectionP listColl, int theList, int theNode) +{ + /* If the list is a singleton, then NIL its pointers and + return NIL for theList*/ + + if (listColl->List[theList].next == theList) + { + listColl->List[theList].prev = listColl->List[theList].next = NIL; + theList = NIL; + } + + /* Join predecessor and successor, dropping theNode from the list. + If theNode is the head of the list, then return the successor as + the new head node. */ + + else + { + int pred = listColl->List[theNode].prev, + succ = listColl->List[theNode].next; + + listColl->List[pred].next = succ; + listColl->List[succ].prev = pred; + + listColl->List[theNode].prev = listColl->List[theNode].next = NIL; + + if (theList == theNode) + theList = succ; + } + + return theList; +} + +#endif // SPEED_MACROS diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.h new file mode 100644 index 000000000..c4ea3bee7 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/listcoll.h @@ -0,0 +1,122 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef _LISTCOLL_H +#define _LISTCOLL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* This include is needed for memset and memcpy */ +#include + + typedef struct + { + int prev, next; + } lcnode; + + typedef struct + { + int N; + lcnode *List; + } listCollectionRec; + + typedef listCollectionRec *listCollectionP; + + listCollectionP LCNew(int N); + void LCFree(listCollectionP *pListColl); + + void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode); + void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode); + +#ifndef SPEED_MACROS + + void LCReset(listCollectionP listColl); + void LCCopy(listCollectionP dst, listCollectionP src); + + int LCGetNext(listCollectionP listColl, int theList, int theNode); + int LCGetPrev(listCollectionP listColl, int theList, int theNode); + + int LCPrepend(listCollectionP listColl, int theList, int theNode); + int LCAppend(listCollectionP listColl, int theList, int theNode); + int LCDelete(listCollectionP listColl, int theList, int theNode); + +#else + +/* void LCReset(listCollectionP listColl); */ + +#define LCReset(listColl) memset(listColl->List, NIL_CHAR, listColl->N * sizeof(lcnode)) + +/* void LCCopy(listCollectionP dst, listCollectionP src) */ + +#define LCCopy(dst, src) memcpy(dst->List, src->List, src->N * sizeof(lcnode)) + +/* int LCGetNext(listCollectionP listColl, int theList, int theNode); + Return theNode's successor, unless it is theList head pointer */ + +#define LCGetNext(listColl, theList, theNode) listColl->List[theNode].next == theList ? NIL : listColl->List[theNode].next + +/* int LCGetPrev(listCollectionP listColl, int theList, int theNode); + Return theNode's predecessor unless theNode is theList head. + To start going backwards, use NIL for theNode, which returns theList head's predecessor + Usage: Obtain last node, loop while NIL not returned, process node then get predecessor. + After theList head processed, get predecessor returns NIL because we started with + theList head's predecessor. */ + +#define LCGetPrev(listColl, theList, theNode) \ + (theNode == NIL \ + ? listColl->List[theList].prev \ + : theNode == theList ? NIL \ + : listColl->List[theNode].prev) + +/* int LCPrepend(listCollectionP listColl, int theList, int theNode); + If theList is empty, then theNode becomes its only member and is returned. + Otherwise, theNode is placed before theList head, and theNode is returned as the new head. */ + +#define LCPrepend(listColl, theList, theNode) \ + (theList == NIL \ + ? (listColl->List[theNode].prev = listColl->List[theNode].next = theNode) \ + : (listColl->List[theNode].next = theList, \ + listColl->List[theNode].prev = listColl->List[theList].prev, \ + listColl->List[listColl->List[theNode].prev].next = theNode, \ + listColl->List[theList].prev = theNode, \ + listColl->List[theList].prev)) + +/* int LCAppend(listCollectionP listColl, int theList, int theNode); + If theList is empty, then theNode becomes its only member and is returned. + Otherwise, theNode is placed before theList head, and then theList head is returned. */ + +#define LCAppend(listColl, theList, theNode) \ + (theList == NIL \ + ? (listColl->List[theNode].prev = listColl->List[theNode].next = theNode) \ + : (listColl->List[theNode].next = theList, \ + listColl->List[theNode].prev = listColl->List[theList].prev, \ + listColl->List[listColl->List[theNode].prev].next = theNode, \ + listColl->List[theList].prev = theNode, \ + theList)) + +/* int LCDelete(listCollectionP listColl, int theList, int theNode); + If theList contains only one node, then NIL it out and return NIL meaning empty list + Otherwise, join the predecessor and successor, then + return either the list head or its successor if the deleted node is the list head + (in that case, the caller makes the successor become the new list head).*/ + +#define LCDelete(listColl, theList, theNode) \ + listColl->List[theList].next == theList \ + ? (listColl->List[theList].prev = listColl->List[theList].next = NIL) \ + : (listColl->List[listColl->List[theNode].prev].next = listColl->List[theNode].next, \ + listColl->List[listColl->List[theNode].next].prev = listColl->List[theNode].prev, \ + (theList == theNode ? listColl->List[theNode].next : theList)) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/platformTime.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/platformTime.h new file mode 100644 index 000000000..91c2ec11a --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/platformTime.h @@ -0,0 +1,54 @@ +#ifndef PLATFORM_TIME +#define PLATFORM_TIME + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +// NOTE: Since platformTime.h is only #include'd by appconst.h, and since appconst.h +// #define's WINDOWS before including platformTime.h, we condition on WINDOWS being defined. +#ifdef WINDOWS + +#include +#include + +#define platform_time DWORD +#define platform_GetTime(timeVar) (timeVar = GetTickCount()) +#define platform_GetDuration(startTime, endTime) ((double)(endTime - startTime) / 1000.0) + +#else + +#include + +typedef struct +{ + clock_t hiresTime; + time_t lowresTime; +} platform_time; + +#define platform_GetTime(timeVar) (timeVar.hiresTime = clock(), timeVar.lowresTime = time(NULL)) + +// Many flavors of Unix have CLOCKS_PER_SEC at 1 million, and clock_t as a 4 byte long integer +// which means that the clock() construct has a resolution of only about 2000 seconds +// If we're getting a duration longer than that, then we fall back to the coarser time() measure + +#define platform_GetDuration(startTime, endTime) ( \ + ((double)(endTime.lowresTime - startTime.lowresTime)) > 2000 ? ((double)(endTime.lowresTime - startTime.lowresTime)) : ((double)(endTime.hiresTime - startTime.hiresTime)) / CLOCKS_PER_SEC) + +/* +#define platform_time clock_t +#define platform_GetTime() clock() +#define platform_GetDuration(startTime, endTime) (((double) (endTime - startTime)) / CLOCKS_PER_SEC) +*/ + +/* +#define platform_time time_t +#define platform_GetTime() time((time_t *)NULL) +#define platform_GetDuration(startTime, endTime) ((double) (endTime - startTime)) +*/ + +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.c new file mode 100644 index 000000000..fc31bde0e --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.c @@ -0,0 +1,224 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "appconst.h" +#include "stack.h" +#include + +stackP sp_New(int capacity) +{ + stackP theStack; + + theStack = (stackP)malloc(sizeof(stack)); + + if (theStack != NULL) + { + theStack->S = (int *)malloc(capacity * sizeof(int)); + if (theStack->S == NULL) + { + free(theStack); + theStack = NULL; + } + } + + if (theStack != NULL) + { + theStack->capacity = capacity; + sp_ClearStack(theStack); + } + + return theStack; +} + +void sp_Free(stackP *pStack) +{ + if (pStack == NULL || *pStack == NULL) + return; + + (*pStack)->capacity = (*pStack)->size = 0; + + if ((*pStack)->S != NULL) + free((*pStack)->S); + (*pStack)->S = NULL; + free(*pStack); + + *pStack = NULL; +} + +int sp_CopyContent(stackP stackDst, stackP stackSrc) +{ + if (stackDst->capacity < stackSrc->size) + return NOTOK; + + if (stackSrc->size > 0) + memcpy(stackDst->S, stackSrc->S, stackSrc->size * sizeof(int)); + + stackDst->size = stackSrc->size; + return OK; +} + +stackP sp_Duplicate(stackP theStack) +{ + stackP newStack = sp_New(theStack->capacity); + + if (newStack == NULL) + return NULL; + + if (theStack->size > 0) + { + memcpy(newStack->S, theStack->S, theStack->size * sizeof(int)); + newStack->size = theStack->size; + } + + return newStack; +} + +int sp_Copy(stackP stackDst, stackP stackSrc) +{ + if (sp_CopyContent(stackDst, stackSrc) != OK) + { + stackP newStack = sp_Duplicate(stackSrc); + int *p; + + if (newStack == NULL) + return NOTOK; + + p = stackDst->S; + stackDst->S = newStack->S; + newStack->S = p; + newStack->capacity = stackDst->capacity; + sp_Free(&newStack); + + stackDst->size = stackSrc->size; + stackDst->capacity = stackSrc->capacity; + } + + return OK; +} + +#ifndef SPEED_MACROS + +int sp_ClearStack(stackP theStack) +{ + theStack->size = 0; + return OK; +} + +int sp_GetCurrentSize(stackP theStack) +{ + return theStack->size; +} + +int sp_SetCurrentSize(stackP theStack, int size) +{ + return size > theStack->capacity ? NOTOK : (theStack->size = size, OK); +} + +int sp_IsEmpty(stackP theStack) +{ + return !theStack->size; +} + +int sp_NonEmpty(stackP theStack) +{ + return theStack->size; +} + +int sp__Push(stackP theStack, int a) +{ + if (theStack->size >= theStack->capacity) + return NOTOK; + + theStack->S[theStack->size++] = a; + return OK; +} + +int sp__Push2(stackP theStack, int a, int b) +{ + if (theStack->size + 1 >= theStack->capacity) + return NOTOK; + + theStack->S[theStack->size++] = a; + theStack->S[theStack->size++] = b; + return OK; +} + +int sp__Pop(stackP theStack, int *pA) +{ + if (theStack->size <= 0) + return NOTOK; + + *pA = theStack->S[--theStack->size]; + return OK; +} + +int sp__Pop_Discard(stackP theStack) +{ + if (theStack->size <= 0) + return NOTOK; + + --theStack->size; + return OK; +} + +int sp__Pop2(stackP theStack, int *pA, int *pB) +{ + if (theStack->size <= 1) + return NOTOK; + + *pB = theStack->S[--theStack->size]; + *pA = theStack->S[--theStack->size]; + + return OK; +} + +int sp__Pop2_Discard1(stackP theStack, int *pA) +{ + if (theStack->size <= 1) + return NOTOK; + + // When a pair of the form (main, secondary) are pushed in order, + // it is sometimes necessary to pop the secondary and discard, + // then pop and store the main datum. + --theStack->size; + *pA = theStack->S[--theStack->size]; + + return OK; +} + +int sp__Pop2_Discard(stackP theStack) +{ + if (theStack->size <= 1) + return NOTOK; + + --theStack->size; + --theStack->size; + + return OK; +} + +int sp_Top(stackP theStack) +{ + return theStack->size ? theStack->S[theStack->size - 1] : NIL; +} + +int sp_Get(stackP theStack, int pos) +{ + if (theStack == NULL || pos < 0 || pos >= theStack->size) + return NOTOK; + + return (theStack->S[pos]); +} + +int sp_Set(stackP theStack, int pos, int val) +{ + if (theStack == NULL || pos < 0 || pos >= theStack->size) + return NOTOK; + + return (theStack->S[pos] = val); +} + +#endif // not defined SPEED_MACROS diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.h new file mode 100644 index 000000000..67db50d3c --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/stack.h @@ -0,0 +1,142 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifndef STACK_H +#define STACK_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +// includes mem functions like memcpy +#include + + typedef struct + { + int *S; + int size, capacity; + } stack; + + typedef stack *stackP; + + stackP sp_New(int); + void sp_Free(stackP *); + + int sp_Copy(stackP, stackP); + + int sp_CopyContent(stackP stackDst, stackP stackSrc); + stackP sp_Duplicate(stackP theStack); + +#define sp_GetCapacity(theStack) (theStack->capacity) + +#ifndef SPEED_MACROS + + int sp_ClearStack(stackP); + int sp_GetCurrentSize(stackP theStack); + int sp_SetCurrentSize(stackP theStack, int top); + + int sp_IsEmpty(stackP); + int sp_NonEmpty(stackP); + +#define sp_Push(theStack, a) \ + { \ + if (sp__Push(theStack, (a)) != OK) \ + return NOTOK; \ + } +#define sp_Push2(theStack, a, b) \ + { \ + if (sp__Push2(theStack, (a), (b)) != OK) \ + return NOTOK; \ + } + + int sp__Push(stackP, int); + int sp__Push2(stackP, int, int); + +#define sp_Pop(theStack, a) \ + { \ + if (sp__Pop(theStack, &(a)) != OK) \ + return NOTOK; \ + } +#define sp_Pop_Discard(theStack) \ + { \ + if (sp__Pop_Discard(theStack) != OK) \ + return NOTOK; \ + } + +#define sp_Pop2(theStack, a, b) \ + { \ + if (sp__Pop2(theStack, &(a), &(b)) != OK) \ + return NOTOK; \ + } +#define sp_Pop2_Discard1(theStack, a) \ + { \ + if (sp__Pop2_Discard1(theStack, &(a)) != OK) \ + return NOTOK; \ + } +#define sp_Pop2_Discard(theStack) \ + { \ + if (sp__Pop2_Discard(theStack) != OK) \ + return NOTOK; \ + } + + int sp__Pop(stackP, int *); + int sp__Pop_Discard(stackP theStack); + + int sp__Pop2(stackP, int *, int *); + int sp__Pop2_Discard1(stackP theStack, int *pA); + int sp__Pop2_Discard(stackP theStack); + + int sp_Top(stackP); + int sp_Get(stackP, int); + int sp_Set(stackP, int, int); + +#else + +#define sp_ClearStack(theStack) theStack->size = 0 +#define sp_GetCurrentSize(theStack) (theStack->size) +#define sp_SetCurrentSize(theStack, Size) ((Size) > theStack->capacity ? NOTOK : (theStack->size = (Size), OK)) + +#define sp_IsEmpty(theStack) !theStack->size +#define sp_NonEmpty(theStack) theStack->size + +#define sp_Push(theStack, a) theStack->S[theStack->size++] = a +#define sp_Push2(theStack, a, b) \ + { \ + sp_Push(theStack, a); \ + sp_Push(theStack, b); \ + } + +#define sp_Pop(theStack, a) a = theStack->S[--theStack->size] +#define sp_Pop_Discard(theStack) --theStack->size + +#define sp_Pop2(theStack, a, b) \ + { \ + sp_Pop(theStack, b); \ + sp_Pop(theStack, a); \ + } +#define sp_Pop2_Discard1(theStack, a) \ + { \ + sp_Pop_Discard(theStack); \ + sp_Pop(theStack, a); \ + } +#define sp_Pop2_Discard(theStack) \ + { \ + sp_Pop_Discard(theStack); \ + sp_Pop_Discard(theStack); \ + } + +#define sp_Top(theStack) (theStack->size ? theStack->S[theStack->size - 1] : NIL) +#define sp_Get(theStack, pos) (theStack->S[pos]) +#define sp_Set(theStack, pos, val) (theStack->S[pos] = val) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c new file mode 100644 index 000000000..a437e4f6f --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c @@ -0,0 +1,1065 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "graphDrawPlanar.h" +#include "graphDrawPlanar.private.h" + +extern int DRAWPLANAR_ID; + +#include "../graph.h" + +#include +#include +#include + +extern void _ClearVisitedFlags(graphP theGraph); + +/* Private functions exported to system */ + +void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink); +int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink); + +int _ComputeVisibilityRepresentation(DrawPlanarContext *context); +int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context); + +/* Private functions */ +int _ComputeVertexPositions(DrawPlanarContext *context); +int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pIndex); +int _ComputeEdgePositions(DrawPlanarContext *context); +int _ComputeVertexRanges(DrawPlanarContext *context); +int _ComputeEdgeRanges(DrawPlanarContext *context); + +/******************************************************************** + _ComputeVisibilityRepresentation() + + Compute vertex positions + Compute edge positions + Assign horizontal ranges of vertices + Assign vertical ranges of edges + + ********************************************************************/ + +int _ComputeVisibilityRepresentation(DrawPlanarContext *context) +{ + if (sp_NonEmpty(context->theGraph->edgeHoles)) + return NOTOK; + + if (_ComputeVertexPositions(context) != OK) + return NOTOK; + + if (_ComputeEdgePositions(context) != OK) + return NOTOK; + + if (_ComputeVertexRanges(context) != OK) + return NOTOK; + + if (_ComputeEdgeRanges(context) != OK) + return NOTOK; + + return OK; +} + +/******************************************************************** + _ComputeVertexPositions() + + Computes the vertex positions in the graph. This method accounts + for disconnected graphs by finding the DFS tree roots and then, + for each, invoking _ComputeVertexPositionsInComponent(). + The index variable for the positioning is maintained by this method + so that the vertices in separate components still get distinct + vertex positions. + ********************************************************************/ + +int _ComputeVertexPositions(DrawPlanarContext *context) +{ + graphP theEmbedding = context->theGraph; + int v, vertpos; + + vertpos = 0; + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + { + // For each DFS tree root in the embedding, we + // compute the vertex positions + if (gp_IsDFSTreeRoot(theEmbedding, v)) + { + if (_ComputeVertexPositionsInComponent(context, v, &vertpos) != OK) + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + _ComputeVertexPositionsInComponent() + + The vertical positions of the vertices are computed based in part + on the information compiled during the planar embedding. + + Each vertex is marked as being between its parent and some ancestor + or beyond the parent relative to the ancestor. The localized, + intuitive notion is that the vertex is either below the parent + or above the parent, but the bicomp containing the vertex, its + parent and the ancestor may be turned upside-down as the result + of a global sequence of operations, resulting in a between or beyond + generalization. + + As the core planarity algorithm constructs successively larger + bicomps out of smaller ones, the bicomp root and its DFS child + are marked as 'tied' in vertex position using markers along the + external face. The marking of the DFS child may be indirect. + Since the child may not be on the external face, its descendant + that is next along the external face is marked instead. + + Later (possibly in the same step or possibly many vertices later), + the Walkdown proceeds around the bicomp and returns to each merge + point, and the tie is broken based on the direction of approach. + + As the Walkdown passes a vertex to its successor, the external + face is short-circuited to remove the vertex from it. Immediately + before this occurs, the new drawing method resolves the tie. Since + the vertex is going to the internal face, its vertex position should + be 'between' its successor and the current vertex being processed + by the Walkdown. + + If the vertex is a child of its external face successor, then it + is simply marked as being 'between' that successor and the current + vertex being processed by the planarity method. But if the vertex + is the parent of its external face successor, then the successor + is placed 'beyond' the vertex. Recall that the successor is either + the DFS child of the vertex or a descendant of that DFS child that + was specially marked because it, not the DFS child, was on the + external face. + + This explains the information that has been collected by the + planarity embedder, which will now be turned into a vertex ordering + system. The idea is to proceed with a pre-order traversal of + the DFS tree, determining the relative orders of the ancestors of + a vertex by the time we get to a vertex. This will allow us to + convert between/beyond into above/below based on the known relative + order of the parent and some given ancestor of the vertex. A vertex + would then be added immediately above or below its parent in the + total ordering, and then the algorithm proceeds to the descendants. + + Consider a depth-first pre-order visitation of vertices. If the + full order of all vertices visited so far is dynamically maintained, + then it is easy to decide whether a vertex goes above or below + its parent based on the between/beyond indicator and the relative + positions in the order of the parent and given ancestor of the + vertex. If the ancestor is above the parent, then 'between' means + put the vertex immediately above its parent and 'beyond' means put + the vertex immediately below its parent in the order. And if the + ancestor is below the parent, then the meaning of between and + beyond are simply reversed. + + Once a vertex is known to be above or below its parent, the drawing + flag is changed from between/beyond to above/below, and processing + proceeds to the next vertex in pre-order depth first search. + + The difficulty lies in keeping an up-to-date topological ordering + that can be queried in constant time to find the relative positions + of two vertices. By itself, this is an instance of "online" or + dynamic topological sorting and has been proven not to be achievable + in linear total time. But this is a special case of the problem and + is therefore solvable through the collection and maintenance of some + additional information. + + Recall that the ancestor V of a vertex is recorded when the setting + for between/beyond is made for a vertex. However, the Walkdown is + invoked on the bicomp rooted by edge (V', C), so the child C of V + that roots the subtree containing the vertex being marked is known. + + Note that when a DFS child is placed above its parent, the entire + DFS subtree of vertices is placed above the parent. Hence, to + determine whether the parent P of a vertex W is above the ancestor + V, where W is marked either between or beyond P and V, we need + only determine the relationship between V and C, which has already + been directly determined due to previous steps of the algorithm + (because V and C are ancestors of P and W). If C is above/below V + then so is P. + + As mentioned above, once the position of P is known relative to V, + it is a simple matter to decide whether to put W above or below P + based on the between/beyond indicator stored in W during embedding. + ********************************************************************/ + +int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pVertpos) +{ + graphP theEmbedding = context->theGraph; + listCollectionP theOrder = LCNew(gp_PrimaryVertexIndexBound(theEmbedding)); + int W, P, C, V, e; + + if (theOrder == NULL) + return NOTOK; + + // Determine the vertex order using a depth first search with + // pre-order visitation. + + sp_ClearStack(theEmbedding->theStack); + sp_Push(theEmbedding->theStack, root); + while (!sp_IsEmpty(theEmbedding->theStack)) + { + sp_Pop(theEmbedding->theStack, W); + + P = gp_GetVertexParent(theEmbedding, W); + V = context->VI[W].ancestor; + C = context->VI[W].ancestorChild; + + // For the special case that we just popped the DFS tree root, + // we simply add the root to its own position. + if (gp_IsNotVertex(P)) + { + // Put the DFS root in the list by itself + LCAppend(theOrder, NIL, W); + // The children of the DFS root have the root as their + // ancestorChild and 'beyond' as the drawingFlag, so this + // causes the root's children to be placed below the root + context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; + } + + // Determine vertex W position relative to P + else + { + // An unresolved tie is an error + if (context->VI[W].drawingFlag == DRAWINGFLAG_TIE) + return NOTOK; + + // If W is the child of a DFS root, then there is no vertex C + // between it and some ancestor V. Both V and C are not a vertex, + // and W will simply take the default of being below its parent. + // If C is a vertex, then it has already been absolutely positioned + // and can be used to help position W relative to its parent P, + // which is equal to or descendant to C. If C below V, then P below V, + // so interpret 'W between P and V' as 'W above P', and interpret + // 'W beyond P relative to V' as 'W below P'. + if (gp_IsNotVertex(C) || context->VI[C].drawingFlag == DRAWINGFLAG_BELOW) + { + if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN) + context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE; + else + context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; + } + + // If C above V, then P above V, so interpret W between + // P and V as W below P, and interpret W beyond P relative + // to V as W above P. + else + { + if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN) + context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; + else + context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE; + } + + if (context->VI[W].drawingFlag == DRAWINGFLAG_BELOW) + LCInsertAfter(theOrder, P, W); + else + LCInsertBefore(theOrder, P, W); + } + + // Push DFS children + e = gp_GetFirstArc(theEmbedding, W); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theEmbedding, e) == EDGE_TYPE_CHILD) + sp_Push(theEmbedding->theStack, gp_GetNeighbor(theEmbedding, e)); + + e = gp_GetNextArc(theEmbedding, e); + } + } + + // Use the order to assign vertical positions + V = root; + while (gp_IsVertex(V)) + { + context->VI[V].pos = *pVertpos; + (*pVertpos)++; + V = LCGetNext(theOrder, root, V); + } + + // Clean up and return + + LCFree(&theOrder); + return OK; +} + +#ifdef LOGGING +/******************************************************************** + _LogEdgeList() + Used to show the progressive calculation of the edge position list. + ********************************************************************/ +void _LogEdgeList(graphP theEmbedding, listCollectionP edgeList, int edgeListHead) +{ + int eIndex = edgeListHead, e, eTwin; + + gp_Log("EdgeList: [ "); + + while (gp_IsArc(eIndex)) + { + e = (eIndex << 1); + eTwin = gp_GetTwinArc(theEmbedding, e); + + gp_Log(gp_MakeLogStr2("(%d, %d) ", + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)), + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eTwin)))); + + eIndex = LCGetNext(edgeList, edgeListHead, eIndex); + } + + gp_LogLine("]"); +} +#endif + +/******************************************************************** + _ComputeEdgePositions() + + Performs a vertical sweep of the combinatorial planar embedding, + developing the edge order in the horizontal sweep line as it + advances through the vertices according to their assigned + vertical positions. + + The 'visitedInfo' member of each vertex is used to indicate the + location in the edge order list of the generator edge for the vertex. + The generator edge is the first edge used to visit the vertex from + a higher vertex in the drawing (i.e. a vertex with an earlier, or + lower, position number). + + All edges added from this vertex to the neighbors below it are + added immediately after the generator edge for the vertex. + ********************************************************************/ + +int _ComputeEdgePositions(DrawPlanarContext *context) +{ + graphP theEmbedding = context->theGraph; + int *vertexOrder = NULL; + listCollectionP edgeList = NULL; + int edgeListHead, edgeListInsertPoint; + int e, eTwin, eCur, v, vpos, epos, eIndex; + + gp_LogLine("\ngraphDrawPlanar.c/_ComputeEdgePositions() start"); + + // Sort the vertices by vertical position (in linear time) + + if ((vertexOrder = (int *)malloc(theEmbedding->N * sizeof(int))) == NULL) + { + return NOTOK; + } + + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + vertexOrder[context->VI[v].pos] = v; + + // Allocate the edge list of size M. + // This is an array of (prev, next) pointers. + // An edge at position X corresponds to the edge + // at position X in the graph structure, which is + // represented by a pair of adjacent edge records + // at index 2X. + + if (theEmbedding->M > 0 && (edgeList = LCNew(gp_GetFirstEdge(theEmbedding) / 2 + theEmbedding->M)) == NULL) + { + free(vertexOrder); + return NOTOK; + } + + edgeListHead = NIL; + + // Each vertex starts out with a NIL generator edge. + + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + gp_SetVertexVisitedInfo(theEmbedding, v, NIL); + + // Perform the vertical sweep of the combinatorial embedding, using + // the vertex ordering to guide the sweep. + // For each vertex, each edge leading to a vertex with a higher number in + // the vertex order is recorded as the "generator edge", or the edge of + // first discovery of that higher numbered vertex, unless the vertex already has + // a recorded generator edge + for (vpos = 0; vpos < theEmbedding->N; vpos++) + { + // Get the vertex associated with the position + v = vertexOrder[vpos]; + gp_LogLine(gp_MakeLogStr3("Processing vertex %d with DFI=%d at position=%d", + gp_GetVertexIndex(theEmbedding, v), v, vpos)); + + // The DFS tree root of a connected component is always the least + // number vertex in the vertex ordering. We have to give it a + // false generator edge so that it is still "visited" and then + // all of its edges are generators for its neighbor vertices because + // they all have greater numbers in the vertex order. + if (gp_IsDFSTreeRoot(theEmbedding, v)) + { + // Set a false generator edge, so the vertex is distinguishable from + // a vertex with no generator edge when its neighbors are visited + // This way, an edge from a neighbor won't get recorded as the + // generator edge of the DFS tree root. + gp_SetVertexVisitedInfo(theEmbedding, v, NIL - 1); + + // Now we traverse the adjacency list of the DFS tree root and + // record each edge as the generator edge of the neighbors + e = gp_GetFirstArc(theEmbedding, v); + while (gp_IsArc(e)) + { + eIndex = (e >> 1); // div by 2 since each edge is a pair of arcs + + edgeListHead = LCAppend(edgeList, edgeListHead, eIndex); + gp_LogLine(gp_MakeLogStr2("Append generator edge (%d, %d) to edgeList", + gp_GetVertexIndex(theEmbedding, v), gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)))); + + // Set the generator edge for the root's neighbor + gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, e), e); + + // Go to the next node of the root's adj list + e = gp_GetNextArc(theEmbedding, e); + } + } + + // Else, if we are not on a DFS tree root... + else + { + // Get the generator edge of the vertex + // Note that this never gets the false generator edge of a DFS tree root + eTwin = gp_GetVertexVisitedInfo(theEmbedding, v); + if (gp_IsNotArc(eTwin)) + return NOTOK; + e = gp_GetTwinArc(theEmbedding, eTwin); + + // Traverse the edges of the vertex, starting + // from the generator edge and going counterclockwise... + + eIndex = (e >> 1); + edgeListInsertPoint = eIndex; + + eCur = gp_GetNextArcCircular(theEmbedding, e); + while (eCur != e) + { + // If the neighboring vertex's position is greater + // than the current vertex (meaning it is lower in the + // diagram), then add that edge to the edge order. + + if (context->VI[gp_GetNeighbor(theEmbedding, eCur)].pos > vpos) + { + eIndex = eCur >> 1; + LCInsertAfter(edgeList, edgeListInsertPoint, eIndex); + + gp_LogLine(gp_MakeLogStr4("Insert (%d, %d) after (%d, %d)", + gp_GetVertexIndex(theEmbedding, v), + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)), + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))), + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)))); + + edgeListInsertPoint = eIndex; + + // If the vertex does not yet have a generator edge, then set it. + // Note that a DFS tree root has a false generator edge, so this if + // test avoids setting a generator edge for a DFS tree root + if (gp_IsNotArc(gp_GetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)))) + { + gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur), eCur); + gp_LogLine(gp_MakeLogStr2("Generator edge (%d, %d)", + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))), + gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)))); + } + } + + // Go to the next node in v's adjacency list + eCur = gp_GetNextArcCircular(theEmbedding, eCur); + } + } + +#ifdef LOGGING + _LogEdgeList(theEmbedding, edgeList, edgeListHead); +#endif + } + + // Now iterate through the edgeList and assign positions to the edges. + epos = 0; + eIndex = edgeListHead; + while (gp_IsArc(eIndex)) + { + e = (eIndex << 1); + eTwin = gp_GetTwinArc(theEmbedding, e); + + context->E[e].pos = context->E[eTwin].pos = epos; + + epos++; + + eIndex = LCGetNext(edgeList, edgeListHead, eIndex); + } + + // Clean up and return + LCFree(&edgeList); + free(vertexOrder); + + gp_LogLine("graphDrawPlanar.c/_ComputeEdgePositions() end\n"); + + return OK; +} + +/******************************************************************** + _ComputeVertexRanges() + + Assumes edge positions are known (see _ComputeEdgePositions()). + A vertex spans horizontally the positions of the edges incident + to it. + ********************************************************************/ + +int _ComputeVertexRanges(DrawPlanarContext *context) +{ + graphP theEmbedding = context->theGraph; + int v, e, min, max; + + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + { + min = theEmbedding->M + 1; + max = -1; + + // Iterate the edges, except in the isolated vertex case we just + // set the min and max to 1 since there no edges controlling where + // it gets drawn. + e = gp_GetFirstArc(theEmbedding, v); + if (gp_IsNotArc(e)) + { + min = max = 0; + } + else + { + while (gp_IsArc(e)) + { + if (min > context->E[e].pos) + min = context->E[e].pos; + + if (max < context->E[e].pos) + max = context->E[e].pos; + + e = gp_GetNextArc(theEmbedding, e); + } + } + + context->VI[v].start = min; + context->VI[v].end = max; + } + + return OK; +} + +/******************************************************************** + _ComputeEdgeRanges() + + Assumes vertex positions are known (see _ComputeVertexPositions()). + An edges spans the vertical range of its endpoints. + ********************************************************************/ + +int _ComputeEdgeRanges(DrawPlanarContext *context) +{ + graphP theEmbedding = context->theGraph; + int e, eTwin, EsizeOccupied, v1, v2, pos1, pos2; + + // Deleted edges are not supported, nor should they be in the embedding, so + // this is just a reality check that avoids an in-use test inside the loop + if (sp_NonEmpty(theEmbedding->edgeHoles)) + return NOTOK; + + EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); + for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e += 2) + { + eTwin = gp_GetTwinArc(theEmbedding, e); + + v1 = gp_GetNeighbor(theEmbedding, e); + v2 = gp_GetNeighbor(theEmbedding, eTwin); + + pos1 = context->VI[v1].pos; + pos2 = context->VI[v2].pos; + + if (pos1 < pos2) + { + context->E[e].start = pos1; + context->E[e].end = pos2; + } + else + { + context->E[e].start = pos2; + context->E[e].end = pos1; + } + + context->E[eTwin].start = context->E[e].start; + context->E[eTwin].end = context->E[e].end; + } + + return OK; +} + +/******************************************************************** + _GetNextExternalFaceVertex() + Uses the extFace links to traverse to the next vertex on the external + face given a current vertex and the link that points to its predecessor. + ********************************************************************/ +int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink) +{ + int nextVertex = gp_GetExtFaceVertex(theGraph, curVertex, 1 ^ *pPrevLink); + + // If the two links in the new vertex are not equal, then only one points + // back to the current vertex, and it is the new prev link. + // Otherwise, the vertex is in a consistently oriented single-edge bicomp, so + // no adjustment of the prev link is needed (due to the consistent orientation). + if (gp_GetExtFaceVertex(theGraph, nextVertex, 0) != gp_GetExtFaceVertex(theGraph, nextVertex, 1)) + { + *pPrevLink = gp_GetExtFaceVertex(theGraph, nextVertex, 0) == curVertex ? 0 : 1; + } + + return nextVertex; +} + +/******************************************************************** + _CollectDrawingData() + To be called by core planarity Walkdown immediately before merging + bicomps and embedding a new back edge. + + Each bicomp is rooted by a DFS tree edge. The parent vertex in + that edge is the bicomp root, and the bicomp contains one DFS child + of the vertex, which is on the child end of the 'root edge'. + + Here we decide whether the DFS child is to be embedded between or + beyond its parent relative to vertex v, the one currently being + processed (and the ancestor endpoint of a back edge being embedded, + where the descendant endpoint is also an endpoint of the bicomp + root being merged). + ********************************************************************/ + +void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink) +{ + graphP theEmbedding = context->theGraph; + int K, Parent, BicompRoot, DFSChild, direction, descendant; + + gp_LogLine("\ngraphDrawPlanar.c/_CollectDrawingData() start"); + gp_LogLine(gp_MakeLogStr3("_CollectDrawingData(RootVertex=%d, W=%d, W_in=%d)", + RootVertex, W, WPrevLink)); + + /* Process all of the merge points to set their drawing flags. */ + + for (K = 0; K < sp_GetCurrentSize(theEmbedding->theStack); K += 4) + { + /* Get the parent and child that are about to be merged from + the 4-tuple in the merge stack */ + Parent = theEmbedding->theStack->S[K]; + BicompRoot = theEmbedding->theStack->S[K + 2]; + DFSChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot); + + /* We get the active descendant vertex in the child bicomp that + will be adjacent to the parent along the external face. + This vertex is guaranteed to be found in one step + due to external face 'short-circuiting' that was done in + step 'Parent' of the planarity algorithm. + We pass theEmbedding->N for the second parameter because + of this; we use this function to signify need of extFace + links in the other implementation.*/ + + direction = theEmbedding->theStack->S[K + 3]; + descendant = _GetNextExternalFaceVertex(theEmbedding, BicompRoot, &direction); + + /* Now we set the tie flag in the DFS child, and mark the + descendant and parent with non-NIL pointers to the child + whose tie flag is to be resolved as soon as one of the + two is connected to by an edge or child bicomp merge. */ + + context->VI[DFSChild].drawingFlag = DRAWINGFLAG_TIE; + + context->VI[descendant].tie[direction] = DFSChild; + + direction = theEmbedding->theStack->S[K + 1]; + context->VI[Parent].tie[direction] = DFSChild; + + gp_LogLine(gp_MakeLogStr5("V[Parent=%d]=.tie[%d] = V[descendant=%d].tie[%d] = (child=%d)", + Parent, direction, descendant, theEmbedding->theStack->S[K + 3], DFSChild)); + } + + gp_LogLine("graphDrawPlanar.c/_CollectDrawingData() end\n"); +} + +/******************************************************************** + _BreakTie() + + The given vertex W has just been arrived at by the core planarity + algorithm. Using WPrevLink, we seek its predecessor WPred on the + external face and test whether the two are involved in a tie that + can be resolved. + + Since the planarity algorithm has just passed by WPred, it is + safe to conclude that WPred can go between W and the current vertex. + + Of course, if W was the parent to some DFS child whose subtree + contains WPred, then the DFS child is marked 'between', placing + the whole subtree including WPred between W and the current vertex. + On the other hand, if WPred was the parent of some DFS child whose + subtree contained W, then we achieve the same effect of putting WPred + 'between' W and the curent vertex by marking the DFS child 'beyond'. + Since the DFS child and hence W are beyond W relative to the current + vertex, WPred is also between W and the current vertex. + + Thus the certain positional relationship between W and WPred + relative to a specific ancestor, the current vertex, is used to + indirectly break the positional tie between MIN(W, WPred) and the + DFS child of MIN(W, WPred) whose subtree contains MAX(W, WPred). + + The ancestorChild is the DFS child of the current vertex whose DFS + subtree contains W and WPred, and it is recorded here in order to + optimize the post-processing calculation of vertex positions. + ********************************************************************/ + +int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink) +{ + graphP theEmbedding = context->theGraph; + + /* First we get the predecessor of W. */ + + int WPredNextLink = 1 ^ WPrevLink, + WPred = _GetNextExternalFaceVertex(theEmbedding, W, &WPredNextLink); + + gp_LogLine("\ngraphDrawPlanar.c/::_BreakTie() start"); + gp_LogLine(gp_MakeLogStr4("_BreakTie(BicompRoot=%d, W=%d, W_in=%d) WPred=%d", + BicompRoot, W, WPrevLink, WPred)); + + /* Ties happen only within a bicomp (i.e. between two non-root vertices) */ + if (gp_IsVirtualVertex(theEmbedding, W) || gp_IsVirtualVertex(theEmbedding, WPred)) + { + gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n"); + return OK; + } + + /* The two vertices are either tied or not; having one tied and the other + not is an error */ + + if (context->VI[W].tie[WPrevLink] != context->VI[WPred].tie[WPredNextLink]) + return NOTOK; + + /* If there is a tie, it can now be resolved. */ + if (gp_IsVertex(context->VI[W].tie[WPrevLink])) + { + int DFSChild = context->VI[W].tie[WPrevLink]; + + /* Set the two ancestor variables that contextualize putting W 'between' + or 'beyond' its parent relative to what. */ + + context->VI[DFSChild].ancestorChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot); + context->VI[DFSChild].ancestor = gp_GetPrimaryVertexFromRoot(theEmbedding, BicompRoot); + + gp_LogLine(gp_MakeLogStr4("V[child=%d]=.ancestorChild = %d, V[child=%d]=.ancestor = %d", + DFSChild, context->VI[DFSChild].ancestorChild, DFSChild, context->VI[DFSChild].ancestor)); + + /* If W is the ancestor of WPred, then the DFSChild subtree contains + WPred, and so must go between W and some ancestor. */ + if (W < WPred) + { + context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BETWEEN; + gp_LogLine(gp_MakeLogStr3("Child=%d is 'between' ancestorChild=%d and ancestor=%d", + DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor)); + } + + /* If W is the descendant, so we achieve the effect of putting WPred + between DFSChild and ancestor by putting the DFSChild 'beyond' WPred. */ + else + { + context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BEYOND; + gp_LogLine(gp_MakeLogStr3("Child=%d is 'beyond' ancestorChild=%d relative to ancestor=%d", + DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor)); + } + + /* The tie is resolved so clear the flags*/ + context->VI[W].tie[WPrevLink] = NIL; + context->VI[WPred].tie[WPredNextLink] = NIL; + } + + gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n"); + return OK; +} + +/******************************************************************** + _RenderToString() + Draws the previously calculated visibility representation in a + string of size (M+1)*2N + 1 characters, which should be deallocated + with free(). + + Returns NULL on failure, or the string containing the visibility + representation otherwise. The string can be printed using %s, + ********************************************************************/ + +char *_RenderToString(graphP theEmbedding) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theEmbedding, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + int N = theEmbedding->N; + int M = theEmbedding->M; + int zeroBasedVertexOffset = (theEmbedding->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theEmbedding) : 0; + int n, m, EsizeOccupied, v, vRange, e, eRange, Mid, Pos; + char *visRep = (char *)malloc(sizeof(char) * ((M + 1) * 2 * N + 1)); + char numBuffer[32]; + + if (visRep == NULL) + return NULL; + + if (sp_NonEmpty(context->theGraph->edgeHoles)) + { + free(visRep); + return NULL; + } + + // Clear the space + for (n = 0; n < N; n++) + { + for (m = 0; m < M; m++) + { + visRep[(2 * n) * (M + 1) + m] = ' '; + visRep[(2 * n + 1) * (M + 1) + m] = ' '; + } + + visRep[(2 * n) * (M + 1) + M] = '\n'; + visRep[(2 * n + 1) * (M + 1) + M] = '\n'; + } + + // Draw the vertices + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + { + Pos = context->VI[v].pos; + for (vRange = context->VI[v].start; vRange <= context->VI[v].end; vRange++) + visRep[(2 * Pos) * (M + 1) + vRange] = '-'; + + // Draw vertex label + Mid = (context->VI[v].start + context->VI[v].end) / 2; + sprintf(numBuffer, "%d", v - zeroBasedVertexOffset); + if ((unsigned)(context->VI[v].end - context->VI[v].start + 1) >= strlen(numBuffer)) + { + memcpy((char *)visRep + (2 * Pos) * (M + 1) + Mid, (char *)numBuffer, strlen(numBuffer)); + } + // If the vertex width is less than the label width, then fail gracefully + else + { + if (strlen(numBuffer) == 2) + visRep[(2 * Pos) * (M + 1) + Mid] = numBuffer[0]; + else + visRep[(2 * Pos) * (M + 1) + Mid] = '*'; + + visRep[(2 * Pos + 1) * (M + 1) + Mid] = numBuffer[strlen(numBuffer) - 1]; + } + } + + // Draw the edges + EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); + for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e += 2) + { + Pos = context->E[e].pos; + for (eRange = context->E[e].start; eRange < context->E[e].end; eRange++) + { + if (eRange > context->E[e].start) + visRep[(2 * eRange) * (M + 1) + Pos] = '|'; + visRep[(2 * eRange + 1) * (M + 1) + Pos] = '|'; + } + } + + // Null terminate string and return it + visRep[(M + 1) * 2 * N] = '\0'; + return visRep; + } + + return NULL; +} + +/******************************************************************** + gp_DrawPlanar_RenderToString() + + Creates a rendition of the planar graph visibility representation + as a string, and return it via the pRenditionString parameter. + The caller can use free() to get rid of the returned string after use. + + Returns NOTOK for any error, OK otherwise. + ********************************************************************/ + +int gp_DrawPlanar_RenderToString(graphP theEmbedding, char **pRenditionString) +{ + if (theEmbedding != NULL && sp_IsEmpty(theEmbedding->edgeHoles) && pRenditionString != NULL) + { + *pRenditionString = _RenderToString(theEmbedding); + return *pRenditionString != NULL ? OK : NOTOK; + } + + return NOTOK; +} + +/******************************************************************** + gp_DrawPlanar_RenderToFile() + + Creates a rendition of the planar graph visibility representation + as a string, then dumps the string to the file. + theFileName - can be "stdout", "stderr" or a file system filename + + Returns NOTOK for any error, OK otherwise. + ********************************************************************/ +int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName) +{ + if (theEmbedding != NULL && sp_IsEmpty(theEmbedding->edgeHoles)) + { + FILE *outfile; + char *theRendition; + + if (strcmp(theFileName, "stdout") == 0) + outfile = stdout; + else if (strcmp(theFileName, "stderr") == 0) + outfile = stderr; + else + outfile = fopen(theFileName, WRITETEXT); + + if (outfile == NULL) + return NOTOK; + + theRendition = _RenderToString(theEmbedding); + if (theRendition != NULL) + { + fprintf(outfile, "%s", theRendition); + free(theRendition); + } + + if (strcmp(theFileName, "stdout") == 0 || strcmp(theFileName, "stderr") == 0) + fflush(outfile); + + else if (fclose(outfile) != 0) + return NOTOK; + + return theRendition ? OK : NOTOK; + } + + return NOTOK; +} + +/******************************************************************** + _CheckVisibilityRepresentationIntegrity() + ********************************************************************/ + +int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context) +{ + graphP theEmbedding = context->theGraph; + int v, e, eTwin, EsizeOccupied, epos, eposIndex; + + if (sp_NonEmpty(context->theGraph->edgeHoles)) + return NOTOK; + + _ClearVisitedFlags(theEmbedding); + + /* Test whether the vertex values make sense and + whether the vertex positions are unique. */ + + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + { + if (theEmbedding->M > 0) + { + if (context->VI[v].pos < 0 || + context->VI[v].pos >= theEmbedding->N || + context->VI[v].start < 0 || + context->VI[v].start > context->VI[v].end || + context->VI[v].end >= theEmbedding->M) + return NOTOK; + } + + // Has the vertex position been used by a vertex before vertex v? + if (gp_GetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding))) + return NOTOK; + + // Mark the vertex position as used by vertex v. + // Note that this marking is made on some other vertex unrelated to v + // We're just reusing the vertex visited array as cheap storage for a + // detector of reusing vertex position integers. + gp_SetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding)); + } + + /* Test whether the edge values make sense and + whether the edge positions are unique */ + + EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); + for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e += 2) + { + /* Each edge has two index locations in the edge information array */ + eTwin = gp_GetTwinArc(theEmbedding, e); + + if (context->E[e].pos != context->E[eTwin].pos || + context->E[e].start != context->E[eTwin].start || + context->E[e].end != context->E[eTwin].end || + context->E[e].pos < 0 || + context->E[e].pos >= theEmbedding->M || + context->E[e].start < 0 || + context->E[e].start > context->E[e].end || + context->E[e].end >= theEmbedding->N) + return NOTOK; + + /* Get the recorded horizontal position of that edge, + a number between 0 and M-1 */ + + epos = context->E[e].pos; + + /* Convert that to an index in the graph structure so we + can use the visited flags in the graph's edges to + tell us whether the positions are being reused. */ + + eposIndex = (epos << 1) + gp_GetFirstEdge(theEmbedding); + eTwin = gp_GetTwinArc(theEmbedding, eposIndex); + + if (gp_GetEdgeVisited(theEmbedding, eposIndex) || gp_GetEdgeVisited(theEmbedding, eTwin)) + return NOTOK; + + gp_SetEdgeVisited(theEmbedding, eposIndex); + gp_SetEdgeVisited(theEmbedding, eTwin); + } + + /* Test whether any edge intersects any vertex position + for a vertex that is not an endpoint of the edge. */ + + EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); + for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e += 2) + { + eTwin = gp_GetTwinArc(theEmbedding, e); + + for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) + { + /* If the vertex is an endpoint of the edge, then... */ + + if (gp_GetNeighbor(theEmbedding, e) == v || gp_GetNeighbor(theEmbedding, eTwin) == v) + { + /* The vertical position of the vertex must be + at the top or bottom of the edge, */ + if (context->E[e].start != context->VI[v].pos && + context->E[e].end != context->VI[v].pos) + return NOTOK; + + /* The horizontal edge position must be in the range of the vertex */ + if (context->E[e].pos < context->VI[v].start || + context->E[e].pos > context->VI[v].end) + return NOTOK; + } + + /* If the vertex is not an endpoint of the edge... */ + + else // if (gp_GetNeighbor(theEmbedding, e) != v && gp_GetNeighbor(theEmbedding, eTwin) != v) + { + /* If the vertical position of the vertex is in the + vertical range of the edge ... */ + + if (context->E[e].start <= context->VI[v].pos && + context->E[e].end >= context->VI[v].pos) + { + /* And if the horizontal position of the edge is in the + horizontal range of the vertex, then return an error. */ + + if (context->VI[v].start <= context->E[e].pos && + context->VI[v].end >= context->E[e].pos) + return NOTOK; + } + } + } + } + + /* All tests passed */ + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.h new file mode 100644 index 000000000..f79ad9b65 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.h @@ -0,0 +1,29 @@ +#ifndef GRAPH_DRAWPLANAR_H +#define GRAPH_DRAWPLANAR_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graphStructures.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define DRAWPLANAR_NAME "DrawPlanar" + + int gp_AttachDrawPlanar(graphP theGraph); + int gp_DetachDrawPlanar(graphP theGraph); + + int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName); + int gp_DrawPlanar_RenderToString(graphP theEmbedding, char **pRenditionString); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.private.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.private.h new file mode 100644 index 000000000..bdcd01758 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.private.h @@ -0,0 +1,91 @@ +#ifndef GRAPH_DRAWPLANAR_PRIVATE_H +#define GRAPH_DRAWPLANAR_PRIVATE_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graph.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + // Additional equipment for each edge + /* + pos, start, end: used to store a visibility representation, or + horvert diagram of a planar graph. + For edges, horizontal position, vertical range + */ + typedef struct + { + int pos, start, end; + } DrawPlanar_EdgeRec; + + typedef DrawPlanar_EdgeRec *DrawPlanar_EdgeRecP; + + // Additional equipment for each vertex + /* + pos, start, end: used to store a visibility representation, or + horvert diagram, of a planar graph. + For vertices, vertical position, horizontal range + drawingFlag, ancestor, ancestorChild: used to collect information needed + to help 'draw' a visibility representation. During planar + embedding, a vertex is determined to be between its DFS parent and + a given ancestor (the vertex being processed) or beyond the parent + relative to the ancestor. In post processing, the relative + orientation of the parent and ancestor are determined, + then the notion of between/beyond resolves to above/below or + below/above depending on whether the ancestor is above or below, + respectively, the parent. The ancestorChild are used to help r + esolve this latter question. + tie[2] stores information along the external face during embedding + that is pertinent to helping break ties in the decisions about + vertical vertex positioning. When vertices are first merged + together into a bicomp, we cannot always decide right away which + vertices will be above or below others. But as we traverse the + external face removing inactive vertices, these positional ties + can be resolved. + */ + typedef struct + { + int pos, start, end; + int drawingFlag, ancestor, ancestorChild; + int tie[2]; + } DrawPlanar_VertexInfo; + + typedef DrawPlanar_VertexInfo *DrawPlanar_VertexInfoP; + +#define DRAWINGFLAG_BEYOND 0 +#define DRAWINGFLAG_TIE 1 +#define DRAWINGFLAG_BETWEEN 2 +#define DRAWINGFLAG_BELOW 3 +#define DRAWINGFLAG_ABOVE 4 + + typedef struct + { + // Helps distinguish initialize from re-initialize + int initialized; + + // The graph that this context augments + graphP theGraph; + + // Parallel array for additional edge level equipment + DrawPlanar_EdgeRecP E; + + // Parallel array for additional vertex level equipment + DrawPlanar_VertexInfoP VI; + + // Overloaded function pointers + graphFunctionTable functions; + + } DrawPlanarContext; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c new file mode 100644 index 000000000..0a87e7a82 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar_Extensions.c @@ -0,0 +1,679 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "graphDrawPlanar.private.h" +#include "graphDrawPlanar.h" + +extern void _ClearVertexVisitedFlags(graphP theGraph, int); + +extern void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink); +extern int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink); + +extern int _ComputeVisibilityRepresentation(DrawPlanarContext *context); +extern int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context); + +/* Forward declarations of local functions */ + +void _DrawPlanar_ClearStructures(DrawPlanarContext *context); +int _DrawPlanar_CreateStructures(DrawPlanarContext *context); +int _DrawPlanar_InitStructures(DrawPlanarContext *context); + +void _DrawPlanar_InitEdgeRec(DrawPlanarContext *context, int v); +void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v); + +/* Forward declarations of overloading functions */ + +int _DrawPlanar_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); +int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); +int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); +int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); +int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); + +int _DrawPlanar_InitGraph(graphP theGraph, int N); +void _DrawPlanar_ReinitializeGraph(graphP theGraph); +int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); +int _DrawPlanar_SortVertices(graphP theGraph); + +int _DrawPlanar_ReadPostprocess(graphP theGraph, char *extraData); +int _DrawPlanar_WritePostprocess(graphP theGraph, char **pExtraData); + +/* Forward declarations of functions used by the extension system */ + +void *_DrawPlanar_DupContext(void *pContext, void *theGraph); +void _DrawPlanar_FreeContext(void *); + +/**************************************************************************** + * DRAWPLANAR_ID - the variable used to hold the integer identifier for this + * extension, enabling this feature's extension context to be distinguished + * from other features' extension contexts that may be attached to a graph. + ****************************************************************************/ + +int DRAWPLANAR_ID = 0; + +/**************************************************************************** + gp_AttachDrawPlanar() + + This function adjusts the graph data structure to attach the planar graph + drawing feature. + + To activate this feature during gp_Embed(), use EMBEDFLAGS_DRAWPLANAR. + + This method may be called immediately after gp_New() in the case of + invoking gp_Read(). For generating graphs, gp_InitGraph() can be invoked + before or after this enabling method. This method detects if the core + graph has already been initialized, and if so, it will initialize the + additional data structures specific to planar graph drawing. This makes + it possible to invoke gp_New() and gp_InitGraph() together, and then attach + this feature only if it is requested at run-time. + + Returns OK for success, NOTOK for failure. + ****************************************************************************/ + +int gp_AttachDrawPlanar(graphP theGraph) +{ + DrawPlanarContext *context = NULL; + + // If the drawing feature has already been attached to the graph, + // then there is no need to attach it again + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + if (context != NULL) + { + return OK; + } + + // Allocate a new extension context + context = (DrawPlanarContext *)malloc(sizeof(DrawPlanarContext)); + if (context == NULL) + { + return NOTOK; + } + + // First, tell the context that it is not initialized + context->initialized = 0; + + // Save a pointer to theGraph in the context + context->theGraph = theGraph; + + // Put the overload functions into the context function table. + // gp_AddExtension will overload the graph's functions with these, and + // return the base function pointers in the context function table + memset(&context->functions, 0, sizeof(graphFunctionTable)); + + context->functions.fpMergeBicomps = _DrawPlanar_MergeBicomps; + context->functions.fpHandleInactiveVertex = _DrawPlanar_HandleInactiveVertex; + context->functions.fpEmbedPostprocess = _DrawPlanar_EmbedPostprocess; + context->functions.fpCheckEmbeddingIntegrity = _DrawPlanar_CheckEmbeddingIntegrity; + context->functions.fpCheckObstructionIntegrity = _DrawPlanar_CheckObstructionIntegrity; + + context->functions.fpInitGraph = _DrawPlanar_InitGraph; + context->functions.fpReinitializeGraph = _DrawPlanar_ReinitializeGraph; + context->functions.fpEnsureArcCapacity = _DrawPlanar_EnsureArcCapacity; + context->functions.fpSortVertices = _DrawPlanar_SortVertices; + + context->functions.fpReadPostprocess = _DrawPlanar_ReadPostprocess; + context->functions.fpWritePostprocess = _DrawPlanar_WritePostprocess; + + _DrawPlanar_ClearStructures(context); + + // Store the Draw context, including the data structure and the + // function pointers, as an extension of the graph + if (gp_AddExtension(theGraph, &DRAWPLANAR_ID, (void *)context, + _DrawPlanar_DupContext, _DrawPlanar_FreeContext, + &context->functions) != OK) + { + _DrawPlanar_FreeContext(context); + return NOTOK; + } + + // Create the Draw-specific structures if the size of the graph is known + // Attach functions are typically invoked after gp_New(), but if a graph + // extension must be attached before gp_Read(), then the attachment + // also happens before gp_InitGraph() because gp_Read() invokes init only + // after it reads the order N of the graph. Hence, this attach call would + // occur when N==0 in the case of gp_Read(). + // But if a feature is attached after gp_InitGraph(), then N > 0 and so we + // need to create and initialize all the custom data structures + if (theGraph->N > 0) + { + if (_DrawPlanar_CreateStructures(context) != OK || + _DrawPlanar_InitStructures(context) != OK) + { + _DrawPlanar_FreeContext(context); + return NOTOK; + } + } + + return OK; +} + +/******************************************************************** + gp_DetachDrawPlanar() + ********************************************************************/ + +int gp_DetachDrawPlanar(graphP theGraph) +{ + return gp_RemoveExtension(theGraph, DRAWPLANAR_ID); +} + +/******************************************************************** + _DrawPlanar_ClearStructures() + ********************************************************************/ + +void _DrawPlanar_ClearStructures(DrawPlanarContext *context) +{ + if (!context->initialized) + { + // Before initialization, the pointers are stray, not NULL + // Once NULL or allocated, free() or LCFree() can do the job + context->E = NULL; + context->VI = NULL; + + context->initialized = 1; + } + else + { + if (context->E != NULL) + { + free(context->E); + context->E = NULL; + } + if (context->VI != NULL) + { + free(context->VI); + context->VI = NULL; + } + } +} + +/******************************************************************** + _DrawPlanar_CreateStructures() + Create uninitialized structures for the vertex and edge levels, + and initialized structures for the graph level + ********************************************************************/ +int _DrawPlanar_CreateStructures(DrawPlanarContext *context) +{ + graphP theGraph = context->theGraph; + int VIsize = gp_PrimaryVertexIndexBound(theGraph); + int Esize = gp_EdgeIndexBound(theGraph); + + if (theGraph->N <= 0) + return NOTOK; + + if ((context->E = (DrawPlanar_EdgeRecP)malloc(Esize * sizeof(DrawPlanar_EdgeRec))) == NULL || + (context->VI = (DrawPlanar_VertexInfoP)malloc(VIsize * sizeof(DrawPlanar_VertexInfo))) == NULL) + { + return NOTOK; + } + + return OK; +} + +/******************************************************************** + _DrawPlanar_InitStructures() + Intended to be called when N>0. + Initializes vertex and edge levels only. Graph level is + already initialized in _CreateStructures() + ********************************************************************/ +int _DrawPlanar_InitStructures(DrawPlanarContext *context) +{ +#ifdef USE_FASTER_1BASEDARRAYS + memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(DrawPlanar_VertexInfo)); + memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(DrawPlanar_EdgeRec)); +#else + int v, e, Esize; + graphP theGraph = context->theGraph; + + if (theGraph->N <= 0) + return NOTOK; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + _DrawPlanar_InitVertexInfo(context, v); + + Esize = gp_EdgeIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) + _DrawPlanar_InitEdgeRec(context, e); +#endif + + return OK; +} + +/******************************************************************** + _DrawPlanar_DupContext() + ********************************************************************/ + +void *_DrawPlanar_DupContext(void *pContext, void *theGraph) +{ + DrawPlanarContext *context = (DrawPlanarContext *)pContext; + DrawPlanarContext *newContext = (DrawPlanarContext *)malloc(sizeof(DrawPlanarContext)); + + if (newContext != NULL) + { + int VIsize = gp_PrimaryVertexIndexBound((graphP)theGraph); + int Esize = gp_EdgeIndexBound((graphP)theGraph); + + *newContext = *context; + + newContext->theGraph = (graphP)theGraph; + + newContext->initialized = 0; + _DrawPlanar_ClearStructures(newContext); + if (((graphP)theGraph)->N > 0) + { + if (_DrawPlanar_CreateStructures(newContext) != OK) + { + _DrawPlanar_FreeContext(newContext); + return NULL; + } + + // Initialize custom data structures by copying + memcpy(newContext->E, context->E, Esize * sizeof(DrawPlanar_EdgeRec)); + memcpy(newContext->VI, context->VI, VIsize * sizeof(DrawPlanar_VertexInfo)); + } + } + + return newContext; +} + +/******************************************************************** + _DrawPlanar_FreeContext() + ********************************************************************/ + +void _DrawPlanar_FreeContext(void *pContext) +{ + DrawPlanarContext *context = (DrawPlanarContext *)pContext; + + _DrawPlanar_ClearStructures(context); + free(pContext); +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_InitGraph(graphP theGraph, int N) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context == NULL) + { + return NOTOK; + } + + theGraph->N = N; + theGraph->NV = N; + if (theGraph->arcCapacity == 0) + theGraph->arcCapacity = 2 * DEFAULT_EDGE_LIMIT * N; + + if (_DrawPlanar_CreateStructures(context) != OK || + _DrawPlanar_InitStructures(context) != OK) + return NOTOK; + + context->functions.fpInitGraph(theGraph, N); + + return OK; +} + +/******************************************************************** + ********************************************************************/ + +void _DrawPlanar_ReinitializeGraph(graphP theGraph) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + // Reinitialize the graph + context->functions.fpReinitializeGraph(theGraph); + + // Do the reinitialization that is specific to this module + _DrawPlanar_InitStructures(context); + } +} + +/******************************************************************** + The current implementation does not support an increase of arc + (edge record) capacity once the extension is attached to the graph + data structure. This is only due to not being necessary to support. + For now, it is easy to ensure the correct capacity before attaching + the extension, but support could be added later if there is some + reason to do so. + ********************************************************************/ + +int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) +{ + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_SortVertices(graphP theGraph) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + // If this is a planarity-based algorithm to which graph drawing has been attached, + // and if the embedding process has already been completed + if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) + { + int v, vIndex; + DrawPlanar_VertexInfo temp; + + // Relabel the context data members that indicate vertices + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (gp_IsVertex(context->VI[v].ancestor)) + { + context->VI[v].ancestor = gp_GetVertexIndex(theGraph, context->VI[v].ancestor); + context->VI[v].ancestorChild = gp_GetVertexIndex(theGraph, context->VI[v].ancestorChild); + } + } + + // "Sort" the extra vertex info associated with each vertex so that it is rearranged according + // to the index values of the vertices. This could be done very easily with an extra array in + // which, for each v, newVI[index of v] = VI[v]. However, this loop avoids memory allocation + // by performing the operation (almost) in-place, except for the pre-existing visitation flags. + _ClearVertexVisitedFlags(theGraph, FALSE); + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + // If the correct data has already been placed into position v + // by prior steps, then skip to the next vertex + if (gp_GetVertexVisited(theGraph, v)) + continue; + + // At the beginning of processing position v, the data in position v + // corresponds to data that belongs at the index of v. + vIndex = gp_GetVertexIndex(theGraph, v); + + // Iterate on position v until it receives the correct data + while (!gp_GetVertexVisited(theGraph, v)) + { + // Place the data at position v into its proper location at position + // vIndex, and move vIndex's data into position v. + temp = context->VI[v]; + context->VI[v] = context->VI[vIndex]; + context->VI[vIndex] = temp; + + // The data at position vIndex is now marked as being correct. + gp_SetVertexVisited(theGraph, vIndex); + + // The data now in position v is the data from position vIndex, + // whose index we now take as the new vIndex + vIndex = gp_GetVertexIndex(theGraph, vIndex); + } + } + } + + if (context->functions.fpSortVertices(theGraph) != OK) + return NOTOK; + + return OK; + } + + return NOTOK; +} + +/******************************************************************** + Returns OK for a successful merge, NOTOK on an internal failure, + or NONEMBEDDABLE if the merge is blocked + ********************************************************************/ + +int _DrawPlanar_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) + { + _CollectDrawingData(context, RootVertex, W, WPrevLink); + } + + return context->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink); + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + int RetVal = context->functions.fpHandleInactiveVertex(theGraph, BicompRoot, pW, pWPrevLink); + + if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) + { + if (_BreakTie(context, BicompRoot, *pW, *pWPrevLink) != OK) + return NOTOK; + } + + return RetVal; + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +void _DrawPlanar_InitEdgeRec(DrawPlanarContext *context, int e) +{ + context->E[e].pos = 0; + context->E[e].start = 0; + context->E[e].end = 0; +} + +/******************************************************************** + ********************************************************************/ + +void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v) +{ + context->VI[v].pos = 0; + context->VI[v].start = 0; + context->VI[v].end = 0; + + context->VI[v].drawingFlag = DRAWINGFLAG_BEYOND; + context->VI[v].ancestorChild = NIL; + context->VI[v].ancestor = NIL; + context->VI[v].tie[0] = NIL; + context->VI[v].tie[1] = NIL; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + int RetVal = context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); + + if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) + { + if (RetVal == OK) + { + RetVal = _ComputeVisibilityRepresentation(context); + } + } + + return RetVal; + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + if (context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph) != OK) + return NOTOK; + + return _CheckVisibilityRepresentationIntegrity(context); + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) +{ + return OK; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_ReadPostprocess(graphP theGraph, char *extraData) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + if (context->functions.fpReadPostprocess(theGraph, extraData) != OK) + return NOTOK; + + else if (extraData != NULL && strlen(extraData) > 0) + { + int v, e, tempInt, EsizeOccupied; + char line[64], tempChar; + + sprintf(line, "<%s>", DRAWPLANAR_NAME); + + // Find the start of the data for this feature + extraData = strstr(extraData, line); + if (extraData == NULL) + return NOTOK; + + // Advance past the start tag + extraData = extraData + strlen(line) + 1; + + // Read the N lines of vertex information + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar, + &context->VI[v].pos, + &context->VI[v].start, + &context->VI[v].end); + + extraData = strchr(extraData, '\n') + 1; + } + + // Read the lines that contain edge information + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) + { + sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar, + &context->E[e].pos, + &context->E[e].start, + &context->E[e].end); + + extraData = strchr(extraData, '\n') + 1; + } + } + + return OK; + } + + return NOTOK; +} + +/******************************************************************** + ********************************************************************/ + +int _DrawPlanar_WritePostprocess(graphP theGraph, char **pExtraData) +{ + DrawPlanarContext *context = NULL; + gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); + + if (context != NULL) + { + if (context->functions.fpWritePostprocess(theGraph, pExtraData) != OK) + return NOTOK; + else + { + int v, e, EsizeOccupied; + char line[64]; + int maxLineSize = 64, extraDataPos = 0; + char *extraData = (char *)calloc((1 + theGraph->N + 2 * theGraph->M + 1) * maxLineSize, sizeof(char)); + int zeroBasedVertexOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; + int zeroBasedEdgeOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstEdge(theGraph) : 0; + + if (extraData == NULL) + return NOTOK; + + // Bit of an unlikely case, but for safety, a bigger maxLineSize + // and line array size are needed to handle very large graphs + if (theGraph->N > 2000000000) + { + free(extraData); + return NOTOK; + } + + sprintf(line, "<%s>\n", DRAWPLANAR_NAME); + strcpy(extraData + extraDataPos, line); + extraDataPos += (int)strlen(line); + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + sprintf(line, "%d: %d %d %d\n", v - zeroBasedVertexOffset, + context->VI[v].pos, + context->VI[v].start, + context->VI[v].end); + strcpy(extraData + extraDataPos, line); + extraDataPos += (int)strlen(line); + } + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) + { + if (gp_EdgeInUse(theGraph, e)) + { + sprintf(line, "%d: %d %d %d\n", e - zeroBasedEdgeOffset, + context->E[e].pos, + context->E[e].start, + context->E[e].end); + strcpy(extraData + extraDataPos, line); + extraDataPos += (int)strlen(line); + } + } + + sprintf(line, "\n", DRAWPLANAR_NAME); + strcpy(extraData + extraDataPos, line); + extraDataPos += (int)strlen(line); + + *pExtraData = extraData; + } + + return OK; + } + + return NOTOK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c new file mode 100644 index 000000000..54362217d --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c @@ -0,0 +1,1373 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include + +#include "../graph.h" + +/* Imported functions */ + +extern void _ClearVertexVisitedFlags(graphP theGraph, int); + +extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); +extern int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R); + +extern void _InitVertexRec(graphP theGraph, int v); + +/* Private functions (some are exported to system only) */ + +int _EmbeddingInitialize(graphP theGraph); + +void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); + +void _InvertVertex(graphP theGraph, int V); +void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R); +int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); + +void _WalkUp(graphP theGraph, int v, int e); +int _WalkDown(graphP theGraph, int v, int RootVertex); + +int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); +void _AdvanceFwdArcList(graphP theGraph, int v, int child, int nextChild); + +int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); +int _OrientVerticesInEmbedding(graphP theGraph); +int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); +int _JoinBicomps(graphP theGraph); + +/******************************************************************** + gp_Embed() + + Either a planar embedding is created in theGraph, or a Kuratowski + subgraph is isolated. Either way, theGraph remains sorted by DFI + since that is the most common desired result. The original vertex + numbers are available in the 'index' members of the vertex records. + Moreover, gp_SortVertices() can be invoked to put the vertices in + the order of the input graph, at which point the 'index' members of + the vertex records will contain the vertex DFIs. + + return OK if the embedding was successfully created or no subgraph + homeomorphic to a topological obstruction was found. + + NOTOK on internal failure + + NONEMBEDDABLE if the embedding couldn't be created due to + the existence of a subgraph homeomorphic to a + topological obstruction. + + For core planarity, OK is returned when theGraph contains a planar + embedding of the input graph, and NONEMBEDDABLE is returned when a + subgraph homeomorphic to K5 or K3,3 has been isolated in theGraph. + + Extension modules can overload functions used by gp_Embed to achieve + alternate algorithms. In those cases, the return results are + similar. For example, a K3,3 search algorithm would return + NONEMBEDDABLE if it finds the K3,3 obstruction, and OK if the graph + is planar or only contains K5 homeomorphs. Similarly, an + outerplanarity module can return OK for an outerplanar embedding or + NONEMBEDDABLE when a subgraph homeomorphic to K2,3 or K4 has been + isolated. + + The algorithm extension for gp_Embed() is encoded in the embedFlags, + and the details of the return value can be found in the extension + module that defines the embedding flag. + ********************************************************************/ + +int gp_Embed(graphP theGraph, int embedFlags) +{ + int v, e, c; + int RetVal = OK; + + // Basic parameter checks + if (theGraph == NULL) + return NOTOK; + + // Preprocessing + theGraph->embedFlags = embedFlags; + + // Allow extension algorithms to postprocess the DFS + if (theGraph->functions.fpEmbeddingInitialize(theGraph) != OK) + return NOTOK; + + // In reverse DFI order, embed the back edges from each vertex to its DFS descendants. + for (v = gp_GetLastVertex(theGraph); gp_VertexInRangeDescending(theGraph, v); v--) + { + RetVal = OK; + + // Walkup calls establish Pertinence in Step v + // Do the Walkup for each cycle edge from v to a DFS descendant W. + e = gp_GetVertexFwdArcList(theGraph, v); + while (gp_IsArc(e)) + { + theGraph->functions.fpWalkUp(theGraph, v, e); + + e = gp_GetNextArc(theGraph, e); + if (e == gp_GetVertexFwdArcList(theGraph, v)) + e = NIL; + } + gp_SetVertexPertinentRootsList(theGraph, v, NIL); + + // Work systematically through the DFS children of vertex v, using Walkdown + // to add the back edges from v to its descendants in each of the DFS subtrees + c = gp_GetVertexSortedDFSChildList(theGraph, v); + while (gp_IsVertex(c)) + { + if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, c))) + { + RetVal = theGraph->functions.fpWalkDown(theGraph, v, gp_GetRootFromDFSChild(theGraph, c)); + // If Walkdown returns OK, then it is OK to proceed with edge addition. + // Otherwise, if Walkdown returns NONEMBEDDABLE then we stop edge addition. + if (RetVal != OK) + break; + } + c = gp_GetVertexNextDFSChild(theGraph, v, c); + } + + // If the Walkdown determined that the graph is NONEMBEDDABLE, + // then the guiding embedder loop can be stopped now. + if (RetVal != OK) + break; + } + + // Postprocessing to orient the embedding and merge any remaining separated bicomps. + // Some extension algorithms may overload this function, e.g. to do nothing if they + // have no need of an embedding. + return theGraph->functions.fpEmbedPostprocess(theGraph, v, RetVal); +} + +/******************************************************************** + _EmbeddingInitialize() + + This method performs the following tasks: + (1) Assign depth first index (DFI) and DFS parentvalues to vertices + (2) Assign DFS edge types + (3) Create a sortedDFSChildList for each vertex, sorted by child DFI + (4) Create a sortedFwdArcList for each vertex, sorted by descendant DFI + (5) Assign leastAncestor values to vertices + (6) Sort the vertices by their DFIs + (7) Initialize for pertinence and future pertinence management + (8) Embed each tree edge as a singleton biconnected component + + The first five of these are performed in a single-pass DFS of theGraph. + Afterward, the vertices are sorted by their DFIs, the lowpoint values + are assigned and then the DFS tree edges stored in virtual vertices + during the DFS are used to create the DFS tree embedding. + ********************************************************************/ +int _EmbeddingInitialize(graphP theGraph) +{ + stackP theStack; + int DFI, v, R, uparent, u, uneighbor, e, f, eTwin, ePrev, eNext; + int leastValue, child; + +#ifdef PROFILE + platform_time start, end; + platform_GetTime(start); +#endif + + gp_LogLine("graphEmbed.c/_EmbeddingInitialize() start\n"); + + theStack = theGraph->theStack; + + // At most we push 2 integers per edge from a vertex to each *unvisited* neighbor + // plus one additional integer to help detect post-processing. This is less + // than the 2 * arcCapacity integer stack that is already in theGraph structure, + // so we make sure it's still there and cleared, then we clear all vertex + // visited flags in prep for the Depth first search operation. */ + + if (sp_GetCapacity(theStack) < 2 * gp_GetArcCapacity(theGraph)) + return NOTOK; + + sp_ClearStack(theStack); + + _ClearVertexVisitedFlags(theGraph, FALSE); + + // This outer loop processes each connected component of a disconnected graph + // No need to compare v < N since DFI will reach N when inner loop processes the + // last connected component in the graph + for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++) + { + // Skip numbered vertices to cause the outerloop to find the + // next DFS tree root in a disconnected graph + if (gp_IsVertex(gp_GetVertexParent(theGraph, v))) + continue; + + // DFS a connected component + sp_Push2(theStack, NIL, NIL); + while (sp_NonEmpty(theStack)) + { + sp_Pop2(theStack, uparent, e); + + // For vertex uparent and edge e, obtain the opposing endpoint u of e + // If uparent is NIL, then e is also NIL and we have encountered the + // false edge to the DFS tree root as pushed above. + u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e); + + // We popped an edge to an unvisited vertex, so it is either a DFS tree edge + // or a false edge to the DFS tree root (u). + if (!gp_GetVertexVisited(theGraph, u)) + { + gp_LogLine(gp_MakeLogStr3("v=%d, DFI=%d, parent=%d", u, DFI, uparent)); + + // (1) Set the DFI and DFS parent + gp_SetVertexVisited(theGraph, u); + gp_SetVertexIndex(theGraph, u, DFI++); + gp_SetVertexParent(theGraph, u, uparent); + + if (gp_IsArc(e)) + { + // (2) Set the edge type values for tree edges + gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD); + gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT); + + // (3) Record u in the sortedDFSChildList of uparent + gp_SetVertexSortedDFSChildList(theGraph, uparent, + gp_AppendDFSChild(theGraph, uparent, gp_GetVertexIndex(theGraph, u))); + + // (8) Record e as the first and last arc of the virtual vertex R, + // a root copy of uparent uniquely associated with child u + R = gp_GetRootFromDFSChild(theGraph, gp_GetVertexIndex(theGraph, u)); + gp_SetFirstArc(theGraph, R, e); + gp_SetLastArc(theGraph, R, e); + } + + // (5) Initialize the least ancestor value + gp_SetVertexLeastAncestor(theGraph, u, gp_GetVertexIndex(theGraph, u)); + + // Push edges to all unvisited neighbors. These will be either + // tree edges to children or forward arcs of back edges + // Edges not pushed are marked as back edges here, except the + // edge leading back to the immediate DFS parent. + e = gp_GetFirstArc(theGraph, u); + while (gp_IsArc(e)) + { + if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) + { + sp_Push2(theStack, u, e); + } + else if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_PARENT) + { + // (2) Set the edge type values for back edges + gp_SetEdgeType(theGraph, e, EDGE_TYPE_BACK); + eTwin = gp_GetTwinArc(theGraph, e); + gp_SetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD); + + // (4) Move the twin of back edge record e to the sortedFwdArcList of the ancestor + uneighbor = gp_GetNeighbor(theGraph, e); + ePrev = gp_GetPrevArc(theGraph, eTwin); + eNext = gp_GetNextArc(theGraph, eTwin); + + if (gp_IsArc(ePrev)) + gp_SetNextArc(theGraph, ePrev, eNext); + else + gp_SetFirstArc(theGraph, uneighbor, eNext); + if (gp_IsArc(eNext)) + gp_SetPrevArc(theGraph, eNext, ePrev); + else + gp_SetLastArc(theGraph, uneighbor, ePrev); + + if (gp_IsArc(f = gp_GetVertexFwdArcList(theGraph, uneighbor))) + { + ePrev = gp_GetPrevArc(theGraph, f); + gp_SetPrevArc(theGraph, eTwin, ePrev); + gp_SetNextArc(theGraph, eTwin, f); + gp_SetPrevArc(theGraph, f, eTwin); + gp_SetNextArc(theGraph, ePrev, eTwin); + } + else + { + gp_SetVertexFwdArcList(theGraph, uneighbor, eTwin); + gp_SetPrevArc(theGraph, eTwin, eTwin); + gp_SetNextArc(theGraph, eTwin, eTwin); + } + + // (5) Update the leastAncestor value for the vertex u + uneighbor = gp_GetVertexIndex(theGraph, uneighbor); + if (uneighbor < gp_GetVertexLeastAncestor(theGraph, u)) + gp_SetVertexLeastAncestor(theGraph, u, uneighbor); + } + + e = gp_GetNextArc(theGraph, e); + } + } + } + } + + // The graph is now DFS numbered + theGraph->internalFlags |= FLAGS_DFSNUMBERED; + + // (6) Now that all vertices have a DFI in the index member, we can sort vertices + if (gp_SortVertices(theGraph) != OK) + return NOTOK; + + // Loop through the vertices and virtual vertices to... + for (v = gp_GetLastVertex(theGraph); gp_VertexInRangeDescending(theGraph, v); v--) + { + // (7) Initialize for pertinence management + gp_SetVertexVisitedInfo(theGraph, v, theGraph->N); + + // (7) Initialize for future pertinence management + child = gp_GetVertexSortedDFSChildList(theGraph, v); + gp_SetVertexFuturePertinentChild(theGraph, v, child); + leastValue = gp_GetVertexLeastAncestor(theGraph, v); + while (gp_IsVertex(child)) + { + if (leastValue > gp_GetVertexLowpoint(theGraph, child)) + leastValue = gp_GetVertexLowpoint(theGraph, child); + + child = gp_GetVertexNextDFSChild(theGraph, v, child); + } + gp_SetVertexLowpoint(theGraph, v, leastValue); + + // (8) Create the DFS tree embedding using the child edge records stored in the virtual vertices + // For each vertex v that is a DFS child, the virtual vertex R that will represent v's parent + // in the singleton bicomp with v is at location v + N in the vertex array. + if (gp_IsDFSTreeRoot(theGraph, v)) + { + gp_SetFirstArc(theGraph, v, NIL); + gp_SetLastArc(theGraph, v, NIL); + } + else + { + R = gp_GetRootFromDFSChild(theGraph, v); + + // Make the child edge the only edge in the virtual vertex adjacency list + e = gp_GetFirstArc(theGraph, R); + gp_SetPrevArc(theGraph, e, NIL); + gp_SetNextArc(theGraph, e, NIL); + + // Reset the twin's neighbor value to point to the virtual vertex + eTwin = gp_GetTwinArc(theGraph, e); + gp_SetNeighbor(theGraph, eTwin, R); + + // Make its twin the only edge in the child's adjacency list + gp_SetFirstArc(theGraph, v, eTwin); + gp_SetLastArc(theGraph, v, eTwin); + gp_SetPrevArc(theGraph, eTwin, NIL); + gp_SetNextArc(theGraph, eTwin, NIL); + + // Set up the external face management data structure to match + gp_SetExtFaceVertex(theGraph, R, 0, v); + gp_SetExtFaceVertex(theGraph, R, 1, v); + gp_SetExtFaceVertex(theGraph, v, 0, R); + gp_SetExtFaceVertex(theGraph, v, 1, R); + } + } + + gp_LogLine("graphEmbed.c/_EmbeddingInitialize() end\n"); + +#ifdef PROFILE + platform_GetTime(end); + printf("Initialize embedding in %.3lf seconds.\n", platform_GetDuration(start, end)); +#endif + + return OK; +} + +/******************************************************************** + _EmbedBackEdgeToDescendant() + The Walkdown has found a descendant vertex W to which it can + attach a back edge up to the root of the bicomp it is processing. + The RootSide and WPrevLink indicate the parts of the external face + that will be replaced at each endpoint of the back edge. + ********************************************************************/ + +void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink) +{ + int fwdArc, backArc, parentCopy; + + /* We get the two edge records of the back edge to embed. + The Walkup recorded in W's adjacentTo the index of the forward arc + from the root's parent copy to the descendant W. */ + + fwdArc = gp_GetVertexPertinentEdge(theGraph, W); + backArc = gp_GetTwinArc(theGraph, fwdArc); + + /* The forward arc is removed from the fwdArcList of the root's parent copy. */ + + parentCopy = gp_GetPrimaryVertexFromRoot(theGraph, RootVertex); + + gp_LogLine(gp_MakeLogStr5("graphEmbed.c/_EmbedBackEdgeToDescendant() V=%d, R=%d, R_out=%d, W=%d, W_in=%d", + parentCopy, RootVertex, RootSide, W, WPrevLink)); + + if (gp_GetVertexFwdArcList(theGraph, parentCopy) == fwdArc) + { + gp_SetVertexFwdArcList(theGraph, parentCopy, gp_GetNextArc(theGraph, fwdArc)); + if (gp_GetVertexFwdArcList(theGraph, parentCopy) == fwdArc) + gp_SetVertexFwdArcList(theGraph, parentCopy, NIL); + } + + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc)); + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc)); + + // The forward arc is added to the adjacency list of the RootVertex. + // Note that we're guaranteed that the RootVertex adjacency list is non-empty, + // so tests for NIL are not needed + gp_SetAdjacentArc(theGraph, fwdArc, 1 ^ RootSide, NIL); + gp_SetAdjacentArc(theGraph, fwdArc, RootSide, gp_GetArc(theGraph, RootVertex, RootSide)); + gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, RootVertex, RootSide), 1 ^ RootSide, fwdArc); + gp_SetArc(theGraph, RootVertex, RootSide, fwdArc); + + // The back arc is added to the adjacency list of W. + // The adjacency list of W is also guaranteed non-empty + gp_SetAdjacentArc(theGraph, backArc, 1 ^ WPrevLink, NIL); + gp_SetAdjacentArc(theGraph, backArc, WPrevLink, gp_GetArc(theGraph, W, WPrevLink)); + gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, W, WPrevLink), 1 ^ WPrevLink, backArc); + gp_SetArc(theGraph, W, WPrevLink, backArc); + + gp_SetNeighbor(theGraph, backArc, RootVertex); + + /* Link the two endpoint vertices together on the external face */ + + gp_SetExtFaceVertex(theGraph, RootVertex, RootSide, W); + gp_SetExtFaceVertex(theGraph, W, WPrevLink, RootVertex); +} + +/******************************************************************** + _InvertVertex() + This function flips the orientation of a single vertex such that + instead of using link successors to go clockwise (or counterclockwise) + around a vertex's adjacency list, link predecessors would be used. + ********************************************************************/ + +void _InvertVertex(graphP theGraph, int W) +{ + int e, temp; + + gp_LogLine(gp_MakeLogStr1("graphEmbed.c/_InvertVertex() W=%d", W)); + + // Swap the links in all the arcs of the adjacency list + e = gp_GetFirstArc(theGraph, W); + while (gp_IsArc(e)) + { + temp = gp_GetNextArc(theGraph, e); + gp_SetNextArc(theGraph, e, gp_GetPrevArc(theGraph, e)); + gp_SetPrevArc(theGraph, e, temp); + + e = temp; + } + + // Swap the first/last edge record indicators in the vertex + temp = gp_GetFirstArc(theGraph, W); + gp_SetFirstArc(theGraph, W, gp_GetLastArc(theGraph, W)); + gp_SetLastArc(theGraph, W, temp); + + // Swap the first/last external face indicators in the vertex + temp = gp_GetExtFaceVertex(theGraph, W, 0); + gp_SetExtFaceVertex(theGraph, W, 0, gp_GetExtFaceVertex(theGraph, W, 1)); + gp_SetExtFaceVertex(theGraph, W, 1, temp); +} + +/******************************************************************** + _MergeVertex() + The merge step joins the vertex W to the root R of a child bicompRoot, + which is a root copy of W appearing in the region N to 2N-1. + + Actually, the first step of this is to redirect all of the edges leading + into R so that they indicate W as the neighbor instead of R. + For each edge node pointing to R, we set the 'v' field to W. Once an + edge is redirected from a root copy R to a parent copy W, the edge is + never redirected again, so we associate the cost of the redirection + as constant per edge, which maintains linear time performance. + + After this is done, a regular circular list union occurs. The only + consideration is that WPrevLink is used to indicate the two edge + records e_w and e_r that will become consecutive in the resulting + adjacency list of W. We set e_w to W's link [WPrevLink] and e_r to + R's link [1^WPrevLink] so that e_w and e_r indicate W and R with + opposing links, which become free to be cross-linked. Finally, + the edge record e_ext, set equal to R's link [WPrevLink], is the edge + that, with e_r, held R to the external face. Now, e_ext will be the + new link [WPrevLink] edge record for W. If e_w and e_r become part + of a proper face, then e_ext and W's link [1^WPrevLink] are the two + edges that attach W to the external face cycle of the containing bicomp. + ********************************************************************/ + +void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R) +{ + int e, eTwin, e_w, e_r, e_ext; + + gp_LogLine(gp_MakeLogStr4("graphEmbed.c/_MergeVertex() W=%d, W_in=%d, R=%d, R_out=%d", + W, WPrevLink, R, 1 ^ WPrevLink)); + + // All arcs leading into R from its neighbors must be changed + // to say that they are leading into W + e = gp_GetFirstArc(theGraph, R); + while (gp_IsArc(e)) + { + eTwin = gp_GetTwinArc(theGraph, e); + gp_GetNeighbor(theGraph, eTwin) = W; + + e = gp_GetNextArc(theGraph, e); + } + + // Obtain the edge records involved in the list union + e_w = gp_GetArc(theGraph, W, WPrevLink); + e_r = gp_GetArc(theGraph, R, 1 ^ WPrevLink); + e_ext = gp_GetArc(theGraph, R, WPrevLink); + + // If W has any edges, then join the list with that of R + if (gp_IsArc(e_w)) + { + // The WPrevLink arc of W is e_w, so the 1^WPrevLink arc in e_w leads back to W. + // Now it must lead to e_r. Likewise, e_r needs to lead back to e_w with the + // opposing link, which is WPrevLink + // Note that the adjacency lists of W and R are guaranteed non-empty, which is + // why these linkages can be made without NIL tests. + gp_SetAdjacentArc(theGraph, e_w, 1 ^ WPrevLink, e_r); + gp_SetAdjacentArc(theGraph, e_r, WPrevLink, e_w); + + // Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext + gp_SetArc(theGraph, W, WPrevLink, e_ext); + gp_SetAdjacentArc(theGraph, e_ext, 1 ^ WPrevLink, NIL); + } + // Otherwise, W just receives R's list. This happens, for example, on a + // DFS tree root vertex during JoinBicomps() + else + { + // Cross-link W's 1^WPrevLink arc and the WPrevLink arc in e_r + gp_SetArc(theGraph, W, 1 ^ WPrevLink, e_r); + gp_SetAdjacentArc(theGraph, e_r, WPrevLink, NIL); + + // Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext + gp_SetArc(theGraph, W, WPrevLink, e_ext); + gp_SetAdjacentArc(theGraph, e_ext, 1 ^ WPrevLink, NIL); + } + + // Erase the entries in R, which is a root copy that is no longer needed + _InitVertexRec(theGraph, R); +} + +/******************************************************************** + _MergeBicomps() + + Merges all biconnected components at the cut vertices indicated by + entries on the stack. + + theGraph contains the stack of bicomp roots and cut vertices to merge + + v, RootVertex, W and WPrevLink are not used in this routine, but are + used by overload extensions + + Returns OK, but an extension function may return a value other than + OK in order to cause Walkdown to terminate immediately. +********************************************************************/ + +int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink) +{ + int R, Rout, Z, ZPrevLink, e, extFaceVertex; + + while (sp_NonEmpty(theGraph->theStack)) + { + sp_Pop2(theGraph->theStack, R, Rout); + sp_Pop2(theGraph->theStack, Z, ZPrevLink); + + /* The external faces of the bicomps containing R and Z will + form two corners at Z. One corner will become part of the + internal face formed by adding the new back edge. The other + corner will be the new external face corner at Z. + We first want to update the links at Z to reflect this. */ + + extFaceVertex = gp_GetExtFaceVertex(theGraph, R, 1 ^ Rout); + gp_SetExtFaceVertex(theGraph, Z, ZPrevLink, extFaceVertex); + + if (gp_GetExtFaceVertex(theGraph, extFaceVertex, 0) == gp_GetExtFaceVertex(theGraph, extFaceVertex, 1)) + // When (R, extFaceVertex) form a singleton bicomp, they have the same orientation, so the Rout link in extFaceVertex + // is the one that has to now point back to Z + gp_SetExtFaceVertex(theGraph, extFaceVertex, Rout, Z); + else + // When R and extFaceVertex are not alone in the bicomp, then they may not have the same orientation, so the + // ext face link that should point to Z is whichever one pointed to R, since R is a root copy of Z. + gp_SetExtFaceVertex(theGraph, extFaceVertex, gp_GetExtFaceVertex(theGraph, extFaceVertex, 0) == R ? 0 : 1, Z); + + /* If the path used to enter Z is opposed to the path + used to exit R, then we have to flip the bicomp + rooted at R, which we signify by inverting R + then setting the sign on its DFS child edge to + indicate that its descendants must be flipped later */ + + if (ZPrevLink == Rout) + { + Rout = 1 ^ ZPrevLink; + + if (gp_GetFirstArc(theGraph, R) != gp_GetLastArc(theGraph, R)) + _InvertVertex(theGraph, R); + + e = gp_GetFirstArc(theGraph, R); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + { + // The core planarity algorithm could simply "set" the inverted flag + // because a bicomp root edge cannot be already inverted in the core + // planarity algorithm at the time of this merge. + // However, extensions may perform edge reductions on tree edges, resulting + // in an inversion sign being promoted to the root edge of a bicomp before + // it gets merged. So, xor is used to reverse the inversion flag on the + // root edge if the bicomp root must be inverted before it is merged. + gp_XorEdgeFlagInverted(theGraph, e); + break; + } + + e = gp_GetNextArc(theGraph, e); + } + } + + // R is no longer pertinent to Z since we are about to merge R into Z, so we delete R + // from Z's pertinent bicomp list (Walkdown gets R from the head of the list). + gp_DeleteVertexPertinentRoot(theGraph, Z, R); + + // If the merge will place the current future pertinence child into the same bicomp as Z, + // then we advance to the next child (or NIL) because future pertinence is + if (gp_GetDFSChildFromRoot(theGraph, R) == gp_GetVertexFuturePertinentChild(theGraph, Z)) + { + gp_SetVertexFuturePertinentChild(theGraph, Z, + gp_GetVertexNextDFSChild(theGraph, Z, gp_GetVertexFuturePertinentChild(theGraph, Z))); + } + + // Now we push R into Z, eliminating R + theGraph->functions.fpMergeVertex(theGraph, Z, ZPrevLink, R); + } + + return OK; +} + +/******************************************************************** + _WalkUp() + v is the vertex currently being embedded + e is the forward arc of the back edge to a descendant W of v + + The Walkup establishes pertinence for step v. It marks W with e + as a way of indicating it is pertinent because it should be made + 'adjacent to' v by adding a back edge (v', W), which will occur when + the Walkdown encounters W. + + The Walkup also determines the pertinent child bicomps that should be + set up as a result of the need to embed edge (v, W). It does this by + recording the pertinent child biconnected components of all cut + vertices between W and the child of v that is an ancestor of W. + Note that it stops the traversal if it finds a visited info value set + to v, which indicates that a prior walkup call in step v has already + done the work. This ensures work is not duplicated. + + A second technique used to maintain a total linear time bound for the + whole planarity method is that of parallel external face traversal. + This ensures that the cost of determining pertinence in step v is + linearly commensurate with the length of the path that ultimately + is removed from the external face. + + Zig and Zag are so named because one goes around one side of a bicomp + and the other goes around the other side, yet we have as yet no notion + of orientation for the bicomp. The edge record e from vertex v gestures + to a descendant vertex W in some other bicomp. Zig and Zag start out + at W. They go around alternate sides of the bicomp until its root is + found. We then hop from the root copy to the parent copy of the vertex + in order to record which bicomp we just came from and also to continue + the walk-up at the parent copy as if it were the new W. We reiterate + this process until the parent copy actually is v, at which point the + Walkup is done. + ********************************************************************/ + +void _WalkUp(graphP theGraph, int v, int e) +{ + int W = gp_GetNeighbor(theGraph, e); + int Zig = W, Zag = W, ZigPrevLink = 1, ZagPrevLink = 0; + int nextZig, nextZag, R; + + // Start by marking W as being directly pertinent + gp_SetVertexPertinentEdge(theGraph, W, e); + + // Zig and Zag are initialized at W, and we continue looping around + // the external faces of bicomps up from W until we reach vertex v + // (or until the visited info optimization breaks the loop) + while (Zig != v) + { + // Obtain the next vertex in a first direction and determine if it is a bicomp root + if (gp_IsVirtualVertex(theGraph, (nextZig = gp_GetExtFaceVertex(theGraph, Zig, 1 ^ ZigPrevLink)))) + { + // If the current vertex along the external face was visited in this step v, + // then the bicomp root and its ancestor roots have already been added. + if (gp_GetVertexVisitedInfo(theGraph, Zig) == v) + break; + + // Store the bicomp root that was found + R = nextZig; + + // Since the bicomp root was the next vertex on the path from Zig, determine the + // vertex on the opposing path that enters the bicomp root. + nextZag = gp_GetExtFaceVertex(theGraph, R, + gp_GetExtFaceVertex(theGraph, R, 0) == Zig ? 1 : 0); + + // If the opposing vertex was already marked visited in this step, then a prior + // Walkup already recorded as pertinent the bicomp root and its ancestor roots. + if (gp_GetVertexVisitedInfo(theGraph, nextZag) == v) + break; + } + + // Obtain the next vertex in the parallel direction and perform the analogous logic + else if (gp_IsVirtualVertex(theGraph, (nextZag = gp_GetExtFaceVertex(theGraph, Zag, 1 ^ ZagPrevLink)))) + { + if (gp_GetVertexVisitedInfo(theGraph, Zag) == v) + break; + R = nextZag; + nextZig = gp_GetExtFaceVertex(theGraph, R, + gp_GetExtFaceVertex(theGraph, R, 0) == Zag ? 1 : 0); + if (gp_GetVertexVisitedInfo(theGraph, nextZig) == v) + break; + } + + // The bicomp root was not found in either direction. + else + { + if (gp_GetVertexVisitedInfo(theGraph, Zig) == v) + break; + if (gp_GetVertexVisitedInfo(theGraph, Zag) == v) + break; + R = NIL; + } + + // This Walkup has now finished with another vertex along each of the parallel + // paths, so they are marked visited in step v so that future Walkups in this + // step v can break if these vertices are encountered again. + gp_SetVertexVisitedInfo(theGraph, Zig, v); + gp_SetVertexVisitedInfo(theGraph, Zag, v); + + // If both directions found new non-root vertices, then proceed with parallel external face traversal + if (gp_IsNotVertex(R)) + { + ZigPrevLink = gp_GetExtFaceVertex(theGraph, nextZig, 0) == Zig ? 0 : 1; + Zig = nextZig; + + ZagPrevLink = gp_GetExtFaceVertex(theGraph, nextZag, 0) == Zag ? 0 : 1; + Zag = nextZag; + } + + // The bicomp root was found and not previously recorded as pertinent, + // so walk up to the parent bicomp and continue + else + { + // Step up from the root (virtual) vertex to the primary (non-virtual) vertex + Zig = Zag = gp_GetPrimaryVertexFromRoot(theGraph, R); + ZigPrevLink = 1; + ZagPrevLink = 0; + + // Add the new root vertex to the list of pertinent bicomp roots of the primary vertex. + // The new root vertex is appended if future pertinent and prepended if only pertinent + // so that, by virtue of storage, the Walkdown will process all pertinent bicomps that + // are not future pertinent before any future pertinent bicomps. + + // NOTE: Unlike vertices, the activity status of a bicomp is computed solely using + // the lowpoint of the DFS child in the bicomp's root edge, which indicates + // whether the DFS child or any of its descendants connect by a back edge to + // ancestors of v. If so, then the bicomp rooted at RootVertex must contain a + // future pertinent vertex that must be kept on the external face. + if (gp_GetVertexLowpoint(theGraph, gp_GetDFSChildFromRoot(theGraph, R)) < v) + gp_AppendVertexPertinentRoot(theGraph, Zig, R); + else + gp_PrependVertexPertinentRoot(theGraph, Zag, R); + } + } +} + +/******************************************************************** + _WalkDown() + Consider a circular shape with small circles and squares along its perimeter. + The small circle at the top is the root vertex of the bicomp. The other small + circles represent active vertices, and the squares represent future pertinent + vertices. The root vertex is a root copy of v, the vertex currently being processed. + + The Walkup previously marked all vertices adjacent to v by setting their + pertinentEdge members with the forward arcs of the back edges to embed. + Two Walkdown traversals are performed to visit all reachable vertices + along each of the external face paths emanating from RootVertex (a root + copy of vertex v) to embed back edges to descendants of vertex v that + have their pertinentEdge members marked. + + During each Walkdown traversal, it is sometimes necessary to hop from a + vertex to one of its child biconnected components in order to reach the + desired vertices. In such cases, the biconnected components are merged + such that adding the back edge forms a new proper face in the biconnected + component rooted at RootVertex (which, again, is a root copy of v). + + The outer loop performs both walks, unless the first walk got all the way + around to RootVertex (only happens when bicomp contains no external activity, + such as when processing the last vertex), or when non-planarity is + discovered (in a pertinent child bicomp such that the stack is non-empty). + + For the inner loop, each iteration visits a vertex W. If W is marked as + requiring a back edge, then MergeBicomps is called to merge the biconnected + components whose cut vertices have been collecting in merge stack. Then, + the back edge (RootVertex, W) is added, and the pertinentEdge of W is cleared. + + Next, we check whether W has a pertinent child bicomp. If so, then we figure + out which path down from the root of the child bicomp leads to the next vertex + to be visited, and we push onto the stack information on the cut vertex and + the paths used to enter into it and exit from it. Alternately, if W + had no pertinent child bicomps, then we check to see if it is inactive. + If so, we find the next vertex along the external face, then short-circuit + its inactive predecessor (under certain conditions). Finally, if W is not + inactive, but it has no pertinent child bicomps, then we already know its + adjacentTo flag is clear so both criteria for internal activity also fail. + Therefore, W must be a stopping vertex. + + A stopping vertex X is a future pertinent vertex that has no pertinent + child bicomps and no unembedded back edge to the current vertex v. + The inner loop of Walkdown stops walking when it reaches a stopping vertex X + because if it were to proceed beyond X and embed a back edge, then X would be + surrounded by the bounding cycle of the bicomp. This would clearly be + incorrect because X has a path leading from it to an ancestor of v, which + would have to cross the bounding cycle. + + Either Walkdown traversal can halt the Walkdown and return if a pertinent + child biconnected component to which the traversal has descended is blocked, + i.e. has stopping vertices on both paths emanating from the root. This + indicates an obstruction to embedding. In core planarity it is evidence of + a K_{3,3}, but some extension algorithms are able to clear the blockage and + proceed with embedding. + + If both Walkdown traversals successfully completed, then the outer loop + ends. Post-processing code tests whether the Walkdown embedded all the + back edges from v to its descendants in the subtree rooted by c, a DFS + child of v uniquely associated with the RootVertex. If not, then embedding + was obstructed. In core planarity it is evidence of a K_{3,3} or K_5, but some + extension algorithms are able to clear the blockage and proceed with embedding. + + Returns OK if all possible edges were embedded, + NONEMBEDDABLE if less than all possible edges were embedded, + NOTOK for an internal code failure + ********************************************************************/ + +int _WalkDown(graphP theGraph, int v, int RootVertex) +{ + int RetVal, W, WPrevLink, R, X, XPrevLink, Y, YPrevLink, RootSide, e; + int RootEdgeChild = gp_GetDFSChildFromRoot(theGraph, RootVertex); + + sp_ClearStack(theGraph->theStack); + + for (RootSide = 0; RootSide < 2; RootSide++) + { + W = gp_GetExtFaceVertex(theGraph, RootVertex, RootSide); + + // Determine the link used to enter W based on which side points back to RootVertex + // Implicitly handled special case: In core planarity, the first Walkdown traversal + // Will be on a singleton edge. In this case, RootVertex and W are *consistently* + // oriented, and the RootSide is 0, so WPrevLink should be 1. This calculation is + // written to implicitly produce that result. + WPrevLink = gp_GetExtFaceVertex(theGraph, W, 1) == RootVertex ? 1 : 0; + + while (W != RootVertex) + { + // Detect unembedded back edge descendant endpoint W + if (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, W))) + { + // Merge any bicomps whose cut vertices were traversed to reach W, then add the + // edge to W to form a new proper face in the embedding. + if (sp_NonEmpty(theGraph->theStack)) + { + if ((RetVal = theGraph->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink)) != OK) + return RetVal; + } + theGraph->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink); + + // Clear W's pertinentEdge since the forward arc it contained has been embedded + gp_SetVertexPertinentEdge(theGraph, W, NIL); + } + + // If W has a pertinent child bicomp, then we descend to the first one... + if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) + { + // Push the vertex W and the direction of entry, then descend to a root copy R of W + sp_Push2(theGraph->theStack, W, WPrevLink); + R = gp_GetVertexFirstPertinentRoot(theGraph, W); + + // Get the next active vertices X and Y on the external face paths emanating from R + X = gp_GetExtFaceVertex(theGraph, R, 0); + XPrevLink = gp_GetExtFaceVertex(theGraph, X, 1) == R ? 1 : 0; + Y = gp_GetExtFaceVertex(theGraph, R, 1); + YPrevLink = gp_GetExtFaceVertex(theGraph, Y, 0) == R ? 0 : 1; + + // Now we implement the Walkdown's simple path selection rules! + // Select a direction from the root to a pertinent vertex, + // preferentially toward a vertex that is not future pertinent + gp_UpdateVertexFuturePertinentChild(theGraph, X, v); + gp_UpdateVertexFuturePertinentChild(theGraph, Y, v); + if (PERTINENT(theGraph, X) && NOTFUTUREPERTINENT(theGraph, X, v)) + { + W = X; + WPrevLink = XPrevLink; + sp_Push2(theGraph->theStack, R, 0); + } + else if (PERTINENT(theGraph, Y) && NOTFUTUREPERTINENT(theGraph, Y, v)) + { + W = Y; + WPrevLink = YPrevLink; + sp_Push2(theGraph->theStack, R, 1); + } + else if (PERTINENT(theGraph, X)) + { + W = X; + WPrevLink = XPrevLink; + sp_Push2(theGraph->theStack, R, 0); + } + else if (PERTINENT(theGraph, Y)) + { + W = Y; + WPrevLink = YPrevLink; + sp_Push2(theGraph->theStack, R, 1); + } + else + { + // Both the X and Y sides of the descendant bicomp are blocked. + // Let the application decide whether it can unblock the bicomp. + // The core planarity/outerplanarity embedder simply isolates a + // planarity/outerplanary obstruction and returns NONEMBEDDABLE + if ((RetVal = theGraph->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R)) != OK) + return RetVal; + + // If an extension algorithm cleared the blockage, then we pop W and WPrevLink + // back off the stack and let the Walkdown traversal try descending again + sp_Pop2(theGraph->theStack, W, WPrevLink); + } + } + else + { + // The vertex W is known to be non-pertinent, so if it is future pertinent + // (or if the algorithm is based on outerplanarity), then the vertex is + // a stopping vertex for the Walkdown traversal. + gp_UpdateVertexFuturePertinentChild(theGraph, W, v); + if (FUTUREPERTINENT(theGraph, W, v) || (theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR)) + { + // Create an external face short-circuit between RootVertex and the stopping vertex W + // so that future steps do not walk down a long path of inactive vertices between them. + // As a special case, we ensure that the external face is not reduced to just two + // vertices, W and RootVertex, because it would then become a challenge to determine + // whether W has the same orientation as RootVertex. + // So, if the other side of RootVertex is already attached to W, then we simply push + // W back one vertex so that the external face will have at least three vertices. + if (gp_GetExtFaceVertex(theGraph, RootVertex, 1 ^ RootSide) == W) + { + X = W; + W = gp_GetExtFaceVertex(theGraph, W, WPrevLink); + WPrevLink = gp_GetExtFaceVertex(theGraph, W, 0) == X ? 1 : 0; + } + gp_SetExtFaceVertex(theGraph, RootVertex, RootSide, W); + gp_SetExtFaceVertex(theGraph, W, WPrevLink, RootVertex); + + // Terminate the Walkdown traversal since it encountered the stopping vertex + break; + } + + // If the vertex is neither pertinent nor future pertinent, then it is inactive. + // The default handler planarity handler simply skips inactive vertices by traversing + // to the next vertex on the external face. + // Once upon a time, false edges called short-circuit edges were added to eliminate + // inactive vertices, but the extFace links above achieve the same result with less work. + else + { + if (theGraph->functions.fpHandleInactiveVertex(theGraph, RootVertex, &W, &WPrevLink) != OK) + return NOTOK; + } + } + } + } + + // Detect and handle the case in which Walkdown was blocked from embedding all the back edges from v + // to descendants in the subtree of the child of v associated with the bicomp RootVertex. + if (gp_IsArc(e = gp_GetVertexFwdArcList(theGraph, v)) && RootEdgeChild < gp_GetNeighbor(theGraph, e)) + { + int nextChild = gp_GetVertexNextDFSChild(theGraph, v, RootEdgeChild); + + // The Walkdown was blocked from embedding all forward arcs into the RootEdgeChild subtree + // if there the next child's DFI is greater than the descendant endpoint of the next forward arc, + // or if there is no next child. + if (gp_IsNotVertex(nextChild) || nextChild > gp_GetNeighbor(theGraph, e)) + { + // If an extension indicates it is OK to proceed despite the unembedded forward arcs, then + // advance to the forward arcs for the next child, if any + if ((RetVal = theGraph->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, RootVertex)) == OK) + _AdvanceFwdArcList(theGraph, v, RootEdgeChild, nextChild); + + return RetVal; + } + } + + return OK; +} + +/******************************************************************** + _HandleBlockedBicomp() + + A biconnected component has blocked the Walkdown from embedding + back edges. Each external face path emanating from the root is + blocked by a stopping vertex. + + The core planarity/outerplanarity algorithm handles the blockage + by isolating an embedding obstruction (a subgraph homeomorphic to + K_{3,3} or K_5 for planarity, or a subgraph homeomorphic to K_{2,3} + or K_4 for outerplanarity). Then NONEMBEDDABLE is returned so that + the WalkDown can terminate. + + Extension algorithms are able to clear some of the blockages, in + which case OK is returned to indicate that the WalkDown can proceed. + + Returns OK to proceed with WalkDown at W, + NONEMBEDDABLE to terminate WalkDown of Root Vertex + NOTOK for internal error + ********************************************************************/ + +int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) +{ + int RetVal = NONEMBEDDABLE; + + if (R != RootVertex) + sp_Push2(theGraph->theStack, R, 0); + + if (theGraph->embedFlags == EMBEDFLAGS_PLANAR) + { + if (_IsolateKuratowskiSubgraph(theGraph, v, RootVertex) != OK) + RetVal = NOTOK; + } + else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR) + { + if (_IsolateOuterplanarObstruction(theGraph, v, RootVertex) != OK) + RetVal = NOTOK; + } + + return RetVal; +} + +/******************************************************************** + _AdvanceFwdArcList() + + If an extension determines that it is OK to leave some forward arcs + unembedded, then we advance the forward arc list head pointer past + the unembedded arcs for the current child so that it points to the + first forward arc for the next child, if any. + + There are two meanings of the phrase "if any". First, there may be + no next child, in which case nextChild is NIL, and the forward arc + list need not be advanced. + + If there is a next child, then the forward arc list head needs to + be advanced to the first arc whose descendant endpoint is greater + than the nextChild, if any. However, the tail end of the forward arc + list may include unembedded forward arcs to a preceding sibling + of the child vertex. So, we advance an edge pointer e until one of + the following happens: + + 1) e gets all the way around to the forward arc list head + 2) e finds an edge whose descendant endpoint is less than the child + 3) e finds an edge whose descendant endpoint is greater than the next child + + In case 1, all the forward arcs belong in the subtree of the child, so + there is no need to change the forward arc list head. + + In case 2, there are no more forward arcs to any following siblings of + the child, only left-behind unembedded forward arcs that we advanced + past in previous calls to this method from Walkdowns of the preceding + children of v. So the forward arc list head should be set to e so that + it is set to the forward arc with the least numbered descendant endpoint. + + In case 3, the desired forward arc into the subtree of a following sibling + of the child has been found, so again the forward arc list head should be + set to e to indicate that edge. + + After all Walkdowns of the children of a vertex, the forward arc list will + be NIL if all edges were embedded, or it will indicate the unembedded + forward arc whose descendant endpoint has the least number. Cases 1 and 2 + directly implement this in cases where a Walkdown for the given child + fails to embed an edge, and case 3 indirectly finishes the job by making + sure the forward arc list head has the right value at the beginning of + a Walkdown for a particular child. If the Walkdown of that child succeeds + at embedding all the forward edges into that child's subtree, then each + embedding advances the forward arc list head. So, even if the Walkdown + of the last pertinent child embeds all forward arcs, then the Walkdown + itself advances the forward arc list head to the first unembedded forward + arc, or to NIL. + ********************************************************************/ + +void _AdvanceFwdArcList(graphP theGraph, int v, int child, int nextChild) +{ + int e = gp_GetVertexFwdArcList(theGraph, v); + + while (gp_IsArc(e)) + { + // 2) e finds an edge whose descendant endpoint is less than the child + if (gp_GetNeighbor(theGraph, e) < child) + { + gp_SetVertexFwdArcList(theGraph, v, e); + break; + } + + // 3) e finds an edge whose descendant endpoint is greater than the next child + else if (gp_IsVertex(nextChild) && nextChild < gp_GetNeighbor(theGraph, e)) + { + gp_SetVertexFwdArcList(theGraph, v, e); + break; + } + + e = gp_GetNextArc(theGraph, e); + // 1) e gets all the way around to the forward arc list head + if (e == gp_GetVertexFwdArcList(theGraph, v)) + e = NIL; + } +} + +/******************************************************************** + _HandleInactiveVertex() + + Although it is possible to short-circuit every inactive vertex from + the external face, for efficiency the Walkdown traversal now just does + a single short-circuit between the bicomp root and a stopping vertex. + This is because the main thing that short-circuiting needs to optimize + is the Walkdown's choice of direction after descending to the root + of a pertinent biconnected component. So, the Walkdown just optimizes + the external face of a biconnected component as it finishes processing + it so it will be ready in future steps when it becomes pertinent. + Hence, when traversing the face of a bicomp during the current step, + we only need to skip an inactive vertex and traverse to the next vertex + on the external face. + ********************************************************************/ + +int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink) +{ + int X = gp_GetExtFaceVertex(theGraph, *pW, 1 ^ *pWPrevLink); + *pWPrevLink = gp_GetExtFaceVertex(theGraph, X, 0) == *pW ? 0 : 1; + *pW = X; + + return OK; +} + +/******************************************************************** + _EmbedPostprocess() + + After the loop that embeds the cycle edges from each vertex to its + DFS descendants, this method is invoked to postprocess the graph. + If the graph is planar or outerplanar, then a consistent orientation + is imposed on the vertices of the embedding, and any remaining + separated biconnected components are joined together. + If the graph is non-planar or non-outerplanar, then an obstruction + to planarity or outerplanarity has already been isolated. + Extensions may override this function to provide alternate behavior. + + @param theGraph - the graph ready for postprocessing + @param v - the last vertex processed by the edge embedding loop + @param edgeEmbeddingResult - + OK if all edge embedding iterations returned OK + NONEMBEDDABLE if an embedding iteration failed to embed + all edges for a vertex + + @return NOTOK on internal failure + NONEMBEDDABLE if a subgraph homeomorphic to a topological + obstruction is isolated in the graph + OK otherwise (e.g. if the graph contains an embedding) + *****************************************************************/ + +int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) +{ + int RetVal = edgeEmbeddingResult; + + // If an embedding was found, then post-process the embedding structure give + // a consistent orientation to all vertices then eliminate virtual vertices + if (edgeEmbeddingResult == OK) + { + if (_OrientVerticesInEmbedding(theGraph) != OK || + _JoinBicomps(theGraph) != OK) + RetVal = NOTOK; + } + + // If the graph is embedded (OK) or NONEMBEDDABLE, we pass the result back + return RetVal; +} + +/******************************************************************** + _OrientVerticesInEmbedding() + + Each vertex will then have an orientation, either clockwise or + counterclockwise. All vertices in each bicomp need to have the + same orientation. + This method clears the stack, and the stack is clear when it + is finished. + Returns OK on success, NOTOK on implementation failure. + ********************************************************************/ + +int _OrientVerticesInEmbedding(graphP theGraph) +{ + int R; + + sp_ClearStack(theGraph->theStack); + + // For each vertex, obtain the associated bicomp root location and, + // if it is still in use as a bicomp root, orient the vertices in the bicomp + for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) + { + if (gp_VirtualVertexInUse(theGraph, R)) + { + if (_OrientVerticesInBicomp(theGraph, R, 0) != OK) + return NOTOK; + } + } + return OK; +} + +/******************************************************************** + _OrientVerticesInBicomp() + As a result of the work done so far, the edges around each vertex have + been put in order, but the orientation may be counterclockwise or + clockwise for different vertices within the same bicomp. + We need to reverse the orientations of those vertices that are not + oriented the same way as the root of the bicomp. + + During embedding, a bicomp with root edge (v', c) may need to be flipped. + We do this by inverting the root copy v' and implicitly inverting the + orientation of the vertices in the subtree rooted by c by assigning -1 + to the sign of the DFSCHILD edge record leading to c. + + We now use these signs to help propagate a consistent vertex orientation + throughout all vertices that have been merged into the given bicomp. + The bicomp root contains the orientation to be imposed on all parent + copy vertices. We perform a standard depth first search to visit each + vertex. A vertex must be inverted if the product of the edge signs + along the tree edges between the bicomp root and the vertex is -1. + + Finally, the PreserveSigns flag, if set, performs the inversions + but does not change any of the edge signs. This allows a second + invocation of this function to restore the state of the bicomp + as it was before the first call. + + This method uses the stack but preserves whatever may have been + on it. In debug mode, it will return NOTOK if the stack overflows. + This method pushes at most two integers per vertext in the bicomp. + + Returns OK on success, NOTOK on implementation failure. + ********************************************************************/ + +int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns) +{ + int W, e, invertedFlag; + int stackBottom = sp_GetCurrentSize(theGraph->theStack); + + sp_Push2(theGraph->theStack, BicompRoot, 0); + + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + /* Pop a vertex to orient */ + sp_Pop2(theGraph->theStack, W, invertedFlag); + + /* Invert the vertex if the inverted flag is set */ + if (invertedFlag) + _InvertVertex(theGraph, W); + + /* Push the vertex's DFS children that are in the bicomp */ + e = gp_GetFirstArc(theGraph, W); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) + { + sp_Push2(theGraph->theStack, gp_GetNeighbor(theGraph, e), + invertedFlag ^ gp_GetEdgeFlagInverted(theGraph, e)); + + if (!PreserveSigns) + gp_ClearEdgeFlagInverted(theGraph, e); + } + + e = gp_GetNextArc(theGraph, e); + } + } + return OK; +} + +/******************************************************************** + _JoinBicomps() + The embedding algorithm works by only joining bicomps once the result + forms a larger bicomp. However, if the original graph was separable + or disconnected, then the result of the embed function will be a + graph that contains each bicomp as a distinct entity. This function + merges the bicomps into one connected graph. + ********************************************************************/ + +int _JoinBicomps(graphP theGraph) +{ + int R; + + for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) + { + // If the root is still active (i.e. an in-use virtual vertex) + // then merge it with its primary (non-virtual) counterpart + if (gp_VirtualVertexInUse(theGraph, R)) + _MergeVertex(theGraph, gp_GetPrimaryVertexFromRoot(theGraph, R), 0, R); + } + + return OK; +} + +/**************************************************************************** + _OrientExternalFacePath() + + The vertices along the path (v ... w) are assumed to be degree two vertices + in an external face path connecting u and x. This method imparts the + orientation of u and x onto the vertices v ... w. + The work done is on the order of the path length. + Returns OK if the external face path was oriented, NOTOK on implementation + error (i.e. if a condition arises providing the path is not on the + external face). + ****************************************************************************/ + +int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x) +{ + int e_u, e_v, e_ulink, e_vlink; + + // Get the edge record in u that indicates v; uses the twinarc method to + // ensure the cost is dominated by the degree of v (which is 2), not u + // (which can be any degree). + e_u = gp_GetTwinArc(theGraph, gp_GetNeighborEdgeRecord(theGraph, v, u)); + + do + { + // Get the external face link in vertex u that indicates the + // edge e_u which connects to the next vertex v in the path + // As a sanity check, we determine whether e_u is an + // external face edge, because there would be an internal + // implementation error if not + if (gp_GetFirstArc(theGraph, u) == e_u) + e_ulink = 0; + else if (gp_GetLastArc(theGraph, u) == e_u) + e_ulink = 1; + else + return NOTOK; + + v = gp_GetNeighbor(theGraph, e_u); + + // Now get the external face link in vertex v that indicates the + // edge e_v which connects back to the prior vertex u. + e_v = gp_GetTwinArc(theGraph, e_u); + + if (gp_GetFirstArc(theGraph, v) == e_v) + e_vlink = 0; + else if (gp_GetLastArc(theGraph, v) == e_v) + e_vlink = 1; + else + return NOTOK; + + // The vertices u and v are inversely oriented if they + // use the same link to indicate the edge [e_u, e_v]. + if (e_vlink == e_ulink) + { + _InvertVertex(theGraph, v); + e_vlink = 1 ^ e_vlink; + } + + // This update of the extFace short-circuit is polite but unnecessary. + // This orientation only occurs once we know we can isolate a K_{3,3}, + // at which point the extFace data structure is not used. + gp_SetExtFaceVertex(theGraph, u, e_ulink, v); + gp_SetExtFaceVertex(theGraph, v, e_vlink, u); + + u = v; + e_u = gp_GetArc(theGraph, v, 1 ^ e_vlink); + } while (u != x); + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c new file mode 100644 index 000000000..4ed8ee723 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c @@ -0,0 +1,833 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define GRAPHISOLATOR_C + +#include "../graph.h" + +/* Imported functions */ + +extern void _ClearVisitedFlags(graphP); + +extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); +extern int _JoinBicomps(graphP theGraph); + +extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); + +/* Private function declarations (exported within system) */ + +int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); + +int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, + int *pAncestor, int *pDescendant); +int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, + int *pDescendant); +int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, + int SubtreeRoot, int *pDescendant); + +int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); + +int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); +void _AddBackEdge(graphP theGraph, int ancestor, int descendant); +int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); + +int _InitializeIsolatorContext(graphP theGraph); + +int _IsolateMinorA(graphP theGraph); +int _IsolateMinorB(graphP theGraph); +int _IsolateMinorC(graphP theGraph); +int _IsolateMinorD(graphP theGraph); +int _IsolateMinorE(graphP theGraph); + +int _IsolateMinorE1(graphP theGraph); +int _IsolateMinorE2(graphP theGraph); +int _IsolateMinorE3(graphP theGraph); +int _IsolateMinorE4(graphP theGraph); + +int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); +int _MarkDFSPathsToDescendants(graphP theGraph); +int _AddAndMarkUnembeddedEdges(graphP theGraph); + +/**************************************************************************** + gp_IsolateKuratowskiSubgraph() + ****************************************************************************/ + +int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R) +{ + int RetVal; + + /* A subgraph homeomorphic to K_{3,3} or K_5 will be isolated by using the visited + flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we + subsequently set visited on all edges and vertices in the homeomorph. */ + + _ClearVisitedFlags(theGraph); + + /* Next, we determine which of the non-planarity Minors was encountered + and the principal bicomp on which the isolator will focus attention. */ + + if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK) + return NOTOK; + + if (_InitializeIsolatorContext(theGraph) != OK) + return NOTOK; + + /* Call the appropriate isolator */ + + if (theGraph->IC.minorType & MINORTYPE_A) + RetVal = _IsolateMinorA(theGraph); + else if (theGraph->IC.minorType & MINORTYPE_B) + RetVal = _IsolateMinorB(theGraph); + else if (theGraph->IC.minorType & MINORTYPE_C) + RetVal = _IsolateMinorC(theGraph); + else if (theGraph->IC.minorType & MINORTYPE_D) + RetVal = _IsolateMinorD(theGraph); + else if (theGraph->IC.minorType & MINORTYPE_E) + RetVal = _IsolateMinorE(theGraph); + else + RetVal = NOTOK; + + /* Delete the unmarked edges and vertices, and return */ + + if (RetVal == OK) + RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph); + + return RetVal; +} + +/**************************************************************************** + _InitializeIsolatorContext() + ****************************************************************************/ + +int _InitializeIsolatorContext(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + /* Obtains the edges connecting X and Y to ancestors of the current vertex */ + + if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE || + _FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE) + return NOTOK; + + /* For Minor B, we seek the last pertinent child biconnected component, which + is also future pertinent, and obtain the DFS child in its root edge. + This child is the subtree root containing vertices with connections to + both the current vertex and an ancestor of the current vertex. */ + + if (theGraph->IC.minorType & MINORTYPE_B) + { + int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); + + IC->uz = gp_GetVertexLowpoint(theGraph, SubtreeRoot); + + if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE || + _FindUnembeddedEdgeToSubtree(theGraph, IC->uz, SubtreeRoot, &IC->dz) != TRUE) + return NOTOK; + } + + /* For all other minors, we obtain an unembedded connecting the current vertex to the + pertinent vertex W, and for minor E we collect the additional unembedded ancestor + connection for the future pertinent vertex Z. */ + + else + { + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + + if (theGraph->IC.minorType & MINORTYPE_E) + if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE) + return NOTOK; + } + + return OK; +} + +/**************************************************************************** + _IsolateMinorA(): Isolate a K3,3 homeomorph + ****************************************************************************/ + +int _IsolateMinorA(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorB(): Isolate a K3,3 homeomorph + ****************************************************************************/ + +int _IsolateMinorB(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), + MAX3(IC->ux, IC->uy, IC->uz)) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorC(): Isolate a K3,3 homeomorph + ****************************************************************************/ + +int _IsolateMinorC(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (gp_GetVertexObstructionType(theGraph, IC->px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW) + { + int highY = gp_GetVertexObstructionType(theGraph, IC->py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW + ? IC->py + : IC->y; + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, highY) != OK) + return NOTOK; + } + else + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->r) != OK) + return NOTOK; + } + + // Note: The x-y path is already marked, due to identifying the type of non-planarity minor + if (_MarkDFSPathsToDescendants(theGraph) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorD(): Isolate a K3,3 homeomorph + ****************************************************************************/ + +int _IsolateMinorD(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + // Note: The x-y and v-z paths are already marked, due to identifying the type of non-planarity minor + if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->y) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorE() + ****************************************************************************/ + +int _IsolateMinorE(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + /* Minor E1: Isolate a K3,3 homeomorph */ + + if (IC->z != IC->w) + return _IsolateMinorE1(theGraph); + + /* Minor E2: Isolate a K3,3 homeomorph */ + + if (IC->uz > MAX(IC->ux, IC->uy)) + return _IsolateMinorE2(theGraph); + + /* Minor E3: Isolate a K3,3 homeomorph */ + + if (IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) + return _IsolateMinorE3(theGraph); + + /* Minor E4: Isolate a K3,3 homeomorph */ + + else if (IC->x != IC->px || IC->y != IC->py) + return _IsolateMinorE4(theGraph); + + /* Minor E: Isolate a K5 homeomorph */ + + // Note: The x-y path is already marked, due to identifying the type of non-planarity minor + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), IC->r) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateMinorE1() + + Reduce to Minor C if the vertex Z responsible for external activity + below the X-Y path does not equal the pertinent vertex W. + ****************************************************************************/ + +int _IsolateMinorE1(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (gp_GetVertexObstructionType(theGraph, IC->z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) + { + gp_ResetVertexObstructionType(theGraph, IC->px, VERTEX_OBSTRUCTIONTYPE_HIGH_RXW); + IC->x = IC->z; + IC->ux = IC->uz; + IC->dx = IC->dz; + } + else if (gp_GetVertexObstructionType(theGraph, IC->z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) + { + gp_ResetVertexObstructionType(theGraph, IC->py, VERTEX_OBSTRUCTIONTYPE_HIGH_RYW); + IC->y = IC->z; + IC->uy = IC->uz; + IC->dy = IC->dz; + } + else + return NOTOK; + + // Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor, + // but the x-y path is also included in minor C, so we let it stay marked since the minor C + // isolator also assumes the x-y path has been marked by non-planarity minor type identification + IC->z = IC->uz = IC->dz = NIL; + theGraph->IC.minorType ^= MINORTYPE_E; + theGraph->IC.minorType |= (MINORTYPE_C | MINORTYPE_E1); + return _IsolateMinorC(theGraph); +} + +/**************************************************************************** + _IsolateMinorE2() + + If uZ (which is the ancestor of v that is adjacent to Z) is a + descendant of both uY and uX, then we reduce to Minor A + ****************************************************************************/ + +int _IsolateMinorE2(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + // Note: The x-y path was already marked, due to identifying E as the type of non-planarity minor, + // but we're reducing to Minor A, which does not include the x-y path, so the visited flags are + // cleared as a convenient, if somewhat wasteful, way to clear the marking on the x-y path + _ClearVisitedFlags(theGraph); + + IC->v = IC->uz; + IC->dw = IC->dz; + IC->z = IC->uz = IC->dz = NIL; + + theGraph->IC.minorType ^= MINORTYPE_E; + theGraph->IC.minorType |= (MINORTYPE_A | MINORTYPE_E2); + return _IsolateMinorA(theGraph); +} + +/**************************************************************************** + _IsolateMinorE3() + ****************************************************************************/ + +int _IsolateMinorE3(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (IC->ux < IC->uy) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->px) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) + return NOTOK; + } + else + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK) + return NOTOK; + } + + // Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor + if (theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), IC->r) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + theGraph->IC.minorType |= MINORTYPE_E3; + return OK; +} + +/**************************************************************************** + _IsolateMinorE4() + ****************************************************************************/ + +int _IsolateMinorE4(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (IC->px != IC->x) + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->w) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK) + return NOTOK; + } + else + { + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->px) != OK || + _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->r) != OK) + return NOTOK; + } + + // Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor + if (theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), + MAX3(IC->ux, IC->uy, IC->uz)) != OK || + _MarkDFSPathsToDescendants(theGraph) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkUnembeddedEdges(theGraph) != OK) + return NOTOK; + + theGraph->IC.minorType |= MINORTYPE_E4; + return OK; +} + +/**************************************************************************** + _GetLeastAncestorConnection() + + This function searches for an ancestor of the current vertex v adjacent by a + cycle edge to the given cutVertex or one of its DFS descendants appearing in + a separated bicomp. The given cutVertex is assumed to be future pertinent + such that either the leastAncestor or the lowpoint of a separated DFS child + is less than v. We obtain the minimum possible connection from the cutVertex + to an ancestor of v. + ****************************************************************************/ + +int _GetLeastAncestorConnection(graphP theGraph, int cutVertex) +{ + int child; + int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); + + child = gp_GetVertexFuturePertinentChild(theGraph, cutVertex); + while (gp_IsVertex(child)) + { + if (gp_IsSeparatedDFSChild(theGraph, child) && + ancestor > gp_GetVertexLowpoint(theGraph, child)) + ancestor = gp_GetVertexLowpoint(theGraph, child); + + child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); + } + + return ancestor; +} + +/**************************************************************************** + _FindUnembeddedEdgeToAncestor() + + This function searches for an ancestor of the current vertex v adjacent by a + cycle edge to the given cutVertex or one of its DFS descendants appearing in + a separated bicomp. + + The given cutVertex is assumed to be future pertinent such that either the + leastAncestor or the lowpoint of a separated DFS child is less than v. + We obtain the minimum possible connection from the cutVertex to an ancestor + of v, then compute the descendant accordingly. + + Returns TRUE if found, FALSE otherwise. + ****************************************************************************/ + +int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, + int *pAncestor, int *pDescendant) +{ + int child, foundChild; + int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); + + child = gp_GetVertexFuturePertinentChild(theGraph, cutVertex); + foundChild = NIL; + while (gp_IsVertex(child)) + { + if (gp_IsSeparatedDFSChild(theGraph, child) && + ancestor > gp_GetVertexLowpoint(theGraph, child)) + { + ancestor = gp_GetVertexLowpoint(theGraph, child); + foundChild = child; + } + child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); + } + + *pAncestor = ancestor; + + // If the least ancestor connection was direct, then return the cutVertex as the descendant + if (ancestor == gp_GetVertexLeastAncestor(theGraph, cutVertex)) + { + *pDescendant = cutVertex; + return TRUE; + } + + // Otherwise find the descendant based on the separated child with least lowpoint + return _FindUnembeddedEdgeToSubtree(theGraph, *pAncestor, foundChild, pDescendant); +} + +/**************************************************************************** + _FindUnembeddedEdgeToCurVertex() + + Given the current vertex v, we search for an edge connecting v to either + a given pertinent vertex W or one of its DFS descendants in the subtree + indicated by the the last pertinent child biconnected component. + Returns TRUE if founds, FALSE otherwise. + ****************************************************************************/ + +int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant) +{ + if (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, cutVertex))) + { + *pDescendant = cutVertex; + return TRUE; + } + else + { + int subtreeRoot = gp_GetVertexFirstPertinentRootChild(theGraph, cutVertex); + + return _FindUnembeddedEdgeToSubtree(theGraph, theGraph->IC.v, + subtreeRoot, pDescendant); + } +} + +/**************************************************************************** + _FindUnembeddedEdgeToSubtree() + + Given the root vertex of a DFS subtree and an ancestor of that subtree, + find a vertex in the subtree that is adjacent to the ancestor by a + cycle edge. + Returns TRUE if found, FALSE if not found. + ****************************************************************************/ + +int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, + int SubtreeRoot, int *pDescendant) +{ + int e, Z, ZNew; + + *pDescendant = NIL; + + /* If SubtreeRoot is a root copy, then we change to the DFS child in the + DFS tree root edge of the bicomp rooted by SubtreeRoot. */ + + SubtreeRoot = gp_IsVirtualVertex(theGraph, SubtreeRoot) + ? gp_GetDFSChildFromRoot(theGraph, SubtreeRoot) + : SubtreeRoot; + + /* Find the least descendant of the cut vertex incident to the ancestor. */ + + e = gp_GetVertexFwdArcList(theGraph, ancestor); + while (gp_IsArc(e)) + { + if (gp_GetNeighbor(theGraph, e) >= SubtreeRoot) + { + if (gp_IsNotVertex(*pDescendant) || *pDescendant > gp_GetNeighbor(theGraph, e)) + *pDescendant = gp_GetNeighbor(theGraph, e); + } + + e = gp_GetNextArc(theGraph, e); + if (e == gp_GetVertexFwdArcList(theGraph, ancestor)) + e = NIL; + } + + if (gp_IsNotVertex(*pDescendant)) + return FALSE; + + /* Make sure the identified descendant actually descends from the cut vertex */ + + Z = *pDescendant; + while (Z != SubtreeRoot) + { + ZNew = gp_GetVertexParent(theGraph, Z); + if (gp_IsNotVertex(ZNew) || ZNew == Z) + return FALSE; + Z = ZNew; + } + + /* Return successfully */ + + return TRUE; +} + +/**************************************************************************** + _MarkPathAlongBicompExtFace() + + Sets the visited flags of vertices and edges on the external face of a + bicomp from startVert to endVert, inclusive, by following the 'first' arc + link out of each visited vertex. + ****************************************************************************/ + +int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert) +{ + int Z, ZPrevLink, ZPrevArc; + + /* Mark the start vertex (and if it is a root copy, mark the parent copy too. */ + + gp_SetVertexVisited(theGraph, startVert); + + /* For each vertex visited after the start vertex, mark the vertex and the + edge used to get there. Stop after marking the ending vertex. */ + + Z = startVert; + ZPrevLink = 1; + do + { + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + + ZPrevArc = gp_GetArc(theGraph, Z, ZPrevLink); + + gp_SetEdgeVisited(theGraph, ZPrevArc); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, ZPrevArc)); + gp_SetVertexVisited(theGraph, Z); + + } while (Z != endVert); + + return OK; +} + +/**************************************************************************** + _MarkDFSPath() + + Sets visited flags of vertices and edges from descendant to ancestor, + including root copy vertices, and including the step of hopping from + a root copy to its parent copy. + + At each vertex, the edge record is obtained whose type indicates that it + leads to the DFS parent. An earlier implementation just used the DFS parent + member of the vertex, but then had to find the edge to mark anyway. + This method is more generalized because some extension algorithms reduce + DFS paths to single DFS tree edges, in which case the edge record with type + EDGE_TYPE_PARENT may indicate the DFS paent or an ancestor. + ****************************************************************************/ +int _MarkDFSPath(graphP theGraph, int ancestor, int descendant) +{ + int e, parent; + + // If we are marking from a root (virtual) vertex upward, then go up to the parent + // copy before starting the loop + if (gp_IsVirtualVertex(theGraph, descendant)) + descendant = gp_GetPrimaryVertexFromRoot(theGraph, descendant); + + // Mark the lowest vertex (the one with the highest number). + gp_SetVertexVisited(theGraph, descendant); + + // Mark all ancestors of the lowest vertex, and the edges used to reach + // them, up to the given ancestor vertex. + while (descendant != ancestor) + { + if (gp_IsNotVertex(descendant)) + return NOTOK; + + // If we are at a bicomp root, then ascend to its parent copy and + // mark it as visited. + if (gp_IsVirtualVertex(theGraph, descendant)) + { + parent = gp_GetPrimaryVertexFromRoot(theGraph, descendant); + } + + // If we are on a regular, non-virtual vertex then get the edge to the parent, + // mark the edge, then fall through to the code that marks the parent vertex. + else + { + // Scan the edges for the one marked as the DFS parent + parent = NIL; + e = gp_GetFirstArc(theGraph, descendant); + while (gp_IsArc(e)) + { + if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) + { + parent = gp_GetNeighbor(theGraph, e); + break; + } + e = gp_GetNextArc(theGraph, e); + } + + // Sanity check on the data structure integrity + if (gp_IsNotVertex(parent)) + return NOTOK; + + // Mark the edge + gp_SetEdgeVisited(theGraph, e); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + } + + // Mark the parent, then hop to the parent and reiterate + gp_SetVertexVisited(theGraph, parent); + descendant = parent; + } + + return OK; +} + +/**************************************************************************** + _MarkDFSPathsToDescendants() + ****************************************************************************/ + +int _MarkDFSPathsToDescendants(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK) + return NOTOK; + + if (gp_IsVertex(IC->dw)) + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK) + return NOTOK; + + if (gp_IsVertex(IC->dz)) + if (theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dz) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _AddAndMarkUnembeddedEdges() + ****************************************************************************/ + +int _AddAndMarkUnembeddedEdges(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK || + _AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK) + return NOTOK; + + if (gp_IsVertex(IC->dw)) + if (_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) + return NOTOK; + + if (gp_IsVertex(IC->dz)) + if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _AddAndMarkEdge() + + Adds edge records for the edge (ancestor, descendant) and marks the edge + records and vertex structures that represent the edge. + ****************************************************************************/ + +int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant) +{ + _AddBackEdge(theGraph, ancestor, descendant); + + /* Mark the edge so it is not deleted */ + + gp_SetVertexVisited(theGraph, ancestor); + gp_SetEdgeVisited(theGraph, gp_GetFirstArc(theGraph, ancestor)); + gp_SetEdgeVisited(theGraph, gp_GetFirstArc(theGraph, descendant)); + gp_SetVertexVisited(theGraph, descendant); + + return OK; +} + +/**************************************************************************** + _AddBackEdge() + + This function transfers the edge records for the edge between the ancestor + and descendant from the forward edge list of the ancestor to the adjacency + lists of the ancestor and descendant. + ****************************************************************************/ + +void _AddBackEdge(graphP theGraph, int ancestor, int descendant) +{ + int fwdArc, backArc; + + /* We get the two edge records of the back edge to embed. */ + + fwdArc = gp_GetVertexFwdArcList(theGraph, ancestor); + while (gp_IsArc(fwdArc)) + { + if (gp_GetNeighbor(theGraph, fwdArc) == descendant) + break; + + fwdArc = gp_GetNextArc(theGraph, fwdArc); + if (fwdArc == gp_GetVertexFwdArcList(theGraph, ancestor)) + fwdArc = NIL; + } + + if (gp_IsNotArc(fwdArc)) + return; + + backArc = gp_GetTwinArc(theGraph, fwdArc); + + /* The forward arc is removed from the fwdArcList of the ancestor. */ + if (gp_GetVertexFwdArcList(theGraph, ancestor) == fwdArc) + { + if (gp_GetNextArc(theGraph, fwdArc) == fwdArc) + gp_SetVertexFwdArcList(theGraph, ancestor, NIL); + else + gp_SetVertexFwdArcList(theGraph, ancestor, gp_GetNextArc(theGraph, fwdArc)); + } + + gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc)); + gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc)); + + /* The forward arc is added to the adjacency list of the ancestor. */ + gp_SetPrevArc(theGraph, fwdArc, NIL); + gp_SetNextArc(theGraph, fwdArc, gp_GetFirstArc(theGraph, ancestor)); + gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, ancestor), fwdArc); + gp_SetFirstArc(theGraph, ancestor, fwdArc); + + /* The back arc is added to the adjacency list of the descendant. */ + gp_SetPrevArc(theGraph, backArc, NIL); + gp_SetNextArc(theGraph, backArc, gp_GetFirstArc(theGraph, descendant)); + gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, descendant), backArc); + gp_SetFirstArc(theGraph, descendant, backArc); + + gp_SetNeighbor(theGraph, backArc, ancestor); +} + +/**************************************************************************** + _DeleteUnmarkedVerticesAndEdges() + + For each vertex, traverse its adjacency list and delete all unvisited edges. + ****************************************************************************/ + +int _DeleteUnmarkedVerticesAndEdges(graphP theGraph) +{ + int v, e; + + /* All of the forward and back arcs of all of the edge records + were removed from the adjacency lists in the planarity algorithm + preprocessing. We now put them back into the adjacency lists + (and we do not mark them), so they can be properly deleted below. */ + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + while (gp_IsArc(e = gp_GetVertexFwdArcList(theGraph, v))) + _AddBackEdge(theGraph, v, gp_GetNeighbor(theGraph, e)); + } + + /* Now we delete all unmarked edges. We don't delete vertices from the + embedding, but the ones we should delete will become degree zero. */ + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_GetEdgeVisited(theGraph, e)) + e = gp_GetNextArc(theGraph, e); + else + e = gp_DeleteEdge(theGraph, e, 0); + } + } + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c new file mode 100644 index 000000000..91707fe4d --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c @@ -0,0 +1,828 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define GRAPHNONPLANAR_C + +#include "../graph.h" + +/* Imported functions */ + +extern void _InitIsolatorContext(graphP theGraph); +extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); +extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); +extern int _HideInternalEdges(graphP theGraph, int vertex); +extern int _RestoreInternalEdges(graphP theGraph, int stackBottom); + +// extern int _OrientVerticesInEmbedding(graphP theGraph); +extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); + +/* Private functions (exported to system) */ + +int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); +int _InitializeNonplanarityContext(graphP theGraph, int v, int R); + +int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); +void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY); +int _FindPertinentVertex(graphP theGraph); +int _SetVertexTypesForMarkingXYPath(graphP theGraph); + +int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom); + +int _MarkHighestXYPath(graphP theGraph); +int _MarkClosestXYPath(graphP theGraph, int targetVertex); +int _MarkZtoRPath(graphP theGraph); +int _FindFuturePertinenceBelowXYPath(graphP theGraph); + +/**************************************************************************** + _ChooseTypeOfNonplanarityMinor() + ****************************************************************************/ + +int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R) +{ + int W, Px, Py, Z; + + /* Create the initial non-planarity minor state in the isolator context */ + + if (_InitializeNonplanarityContext(theGraph, v, R) != OK) + return NOTOK; + + R = theGraph->IC.r; + W = theGraph->IC.w; + + /* If the root copy is not a root copy of the current vertex v, + then the Walkdown terminated because it couldn't find + a viable path along a child bicomp, which is Minor A. */ + + if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) + { + theGraph->IC.minorType |= MINORTYPE_A; + return OK; + } + + /* If W has a pertinent and future pertinent child bicomp, then we've found Minor B */ + + if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) + { + if (gp_GetVertexLowpoint(theGraph, gp_GetVertexLastPertinentRootChild(theGraph, W)) < v) + { + theGraph->IC.minorType |= MINORTYPE_B; + return OK; + } + } + + /* Find the highest obstructing X-Y path */ + + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + + Px = theGraph->IC.px; + Py = theGraph->IC.py; + + /* If either point of attachment is 'high' (P_x closer to R than X + or P_y closer to R than Y along external face), then we've + matched Minor C. */ + + if (gp_GetVertexObstructionType(theGraph, Px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW || + gp_GetVertexObstructionType(theGraph, Py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW) + { + theGraph->IC.minorType |= MINORTYPE_C; + return OK; + } + + /* For Minor D, we search for a path from an internal + vertex Z along the X-Y path up to the root R of the bicomp. */ + + if (_MarkZtoRPath(theGraph) != OK) + return NOTOK; + + if (gp_IsVertex(theGraph->IC.z)) + { + theGraph->IC.minorType |= MINORTYPE_D; + return OK; + } + + /* For Minor E, we search for an future pertinent vertex Z + below the points of attachment of the X-Y path */ + + Z = _FindFuturePertinenceBelowXYPath(theGraph); + if (gp_IsVertex(Z)) + { + theGraph->IC.z = Z; + theGraph->IC.minorType |= MINORTYPE_E; + return OK; + } + + return NOTOK; +} + +/**************************************************************************** + _InitializeNonplanarityContext() + + This method finds the stopping vertices X and Y, and the pertinent vertex W + of a bicomp rooted by vertex R. + + If R is NIL, the routine first determines which bicomp produced non-planarity + condition. If the stack is non-empty, then R is on the top of the stack. + Otherwise, an unembedded fwdArc from the fwdArcList of vertex v is used in + combination with the sortedDFSChildList of v to determine R. + + If the parameter R was not NIL, then this method assumes it must operate + only on the bicomp rooted by R, and it also assumes that the caller has + not cleared the visited flags in the bicomp, so they are cleared. + + This routine imparts consistent orientation to all vertices in bicomp R + since several subroutines count on this. The edge signs are preserved so that + the original orientations of all vertices can be restored. If the vertices + of the embedding are already consistently oriented, then this operation + simply has no effect. + + Finally, in the bicomp R, the vertex types of all non-root vertices on the + external face are classified according to whether or not they are closer to + the root R than X and Y along the external face paths (R X W) and (R Y W). + ****************************************************************************/ + +int _InitializeNonplanarityContext(graphP theGraph, int v, int R) +{ + // Blank out the isolator context, then assign the input graph reference + // and the current vertext v into the context. + _InitIsolatorContext(theGraph); + theGraph->IC.v = v; + + // The bicomp root provided was the one on which the WalkDown was performed, + // but in the case of Minor A, the central bicomp of the minor is at the top + // of the stack, so R must be changed to that value. + if (sp_NonEmpty(theGraph->theStack)) + { + // The top of stack has the pair R and 0/1 direction Walkdown traversal proceeds from R + // Need only R, so pop and discard the direction, then pop R + sp_Pop2_Discard1(theGraph->theStack, R); + } + + theGraph->IC.r = R; + + // A number of subroutines require the main bicomp of the minor to be + // consistently oriented and its visited flags clear. + if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) + { + return NOTOK; + } + + if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) + return NOTOK; + + // Now we find the active vertices along both external face paths + // extending from R. + _FindActiveVertices(theGraph, R, &theGraph->IC.x, &theGraph->IC.y); + + // Now, we obtain the pertinent vertex W on the lower external face + // path between X and Y (that path that does not include R). + theGraph->IC.w = _FindPertinentVertex(theGraph); + + // Now we can classify the vertices along the external face of the bicomp + // rooted at R as 'high RXW', 'low RXW', 'high RXY', 'low RXY' + if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) + return NOTOK; + + // All work is done, so return success + return OK; +} + +/******************************************************************** + _GetNeighborOnExtFace() + + Each vertex contains two 'link' index pointers that indicate the + first and last adjacency list arc. If the vertex is on the external face, + then these two arcs are also on the external face. We want to take one of + those edges to get to the next vertex on the external face. + On input *pPrevLink indicates which link we followed to arrive at + curVertex. On output *pPrevLink will be set to the link we follow to + get into the next vertex. + To get to the next vertex, we use the opposite link from the one used + to get into curVertex. This takes us to an edge node. The twinArc + of that edge node, carries us to an edge node in the next vertex. + At least one of the two links in that edge node will lead to a vertex + node in G, which is the next vertex. Once we arrive at the next + vertex, at least one of its links will lead back to the edge node, and + that link becomes the output value of *pPrevLink. + + NOTE: This method intentionally ignores the extFace optimization + links. It is invoked when the "real" external face must be + traversed and hence when the constant time guarantee is not + needed from the extFace short-circuit that connects the + bicomp root to the first active vertices along each external + face path emanating from the bicomp root. + ********************************************************************/ + +int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink) +{ + /* Exit curVertex from whichever link was not previously used to enter it */ + + int arc = gp_GetArc(theGraph, curVertex, 1 ^ (*pPrevLink)); + int nextVertex = gp_GetNeighbor(theGraph, arc); + + /* This if stmt assigns the new prev link that tells us which edge + record was used to enter nextVertex (so that we exit from the + opposing edge record). + + However, if we are in a singleton bicomp, then both links in nextVertex + lead back to curVertex. We want the two arcs of a singleton bicomp to + act like a cycle, so we just don't change the prev link in this case. + + But when nextVertex has more than one edge, we need to figure out + whether the first edge or last edge (which are the two on the external + face) was used to enter nextVertex so we can exit from the other one + as traversal of the external face continues later. */ + + if (gp_GetFirstArc(theGraph, nextVertex) != gp_GetLastArc(theGraph, nextVertex)) + *pPrevLink = gp_GetTwinArc(theGraph, arc) == gp_GetFirstArc(theGraph, nextVertex) ? 0 : 1; + + return nextVertex; +} + +/**************************************************************************** + _FindActiveVertices() + + Descends from the root of a bicomp R along both external face paths (which + are indicated by the first and last arcs in R's adjacency list), returning + the first active vertex appearing in each direction. + ****************************************************************************/ + +void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY) +{ + int XPrevLink = 1, YPrevLink = 0, v = theGraph->IC.v; + + *pX = _GetNeighborOnExtFace(theGraph, R, &XPrevLink); + *pY = _GetNeighborOnExtFace(theGraph, R, &YPrevLink); + + // For planarity algorithms, advance past inactive vertices + // For outerplanarity algorithms, ignore the notion of inactive vertices + // since all vertices must remain on the external face. + if (!(theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR)) + { + gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v); + while (INACTIVE(theGraph, *pX, v)) + { + *pX = _GetNeighborOnExtFace(theGraph, *pX, &XPrevLink); + gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v); + } + + gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v); + while (INACTIVE(theGraph, *pY, v)) + { + *pY = _GetNeighborOnExtFace(theGraph, *pY, &YPrevLink); + gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v); + } + } +} + +/**************************************************************************** + _FindPertinentVertex() + + Get the first vertex after x. Since x was obtained using a prevlink of 1 on r, + we use the same prevlink so we don't go back to R. + Then, we proceed around the lower path until we find a vertex W that either + has pertinent child bicomps or is directly adjacent to the current vertex v. + ****************************************************************************/ + +int _FindPertinentVertex(graphP theGraph) +{ + int W = theGraph->IC.x, WPrevLink = 1; + + W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); + + while (W != theGraph->IC.y) + { + if (PERTINENT(theGraph, W)) + return W; + + W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); + } + + return NIL; +} + +/**************************************************************************** + _SetVertexTypesForMarkingXYPath() + + Label the vertices along the external face of the bicomp rooted at R as + 'high RXW', 'low RXW', 'high RXY', 'low RXY' + ****************************************************************************/ + +int _SetVertexTypesForMarkingXYPath(graphP theGraph) +{ + int R, X, Y, W, Z, ZPrevLink, ZType; + + // Unpack the context for efficiency of loops + R = theGraph->IC.r; + X = theGraph->IC.x; + Y = theGraph->IC.y; + W = theGraph->IC.w; + + // Ensure basic preconditions of this routine are met + if (gp_IsNotVertex(R) || gp_IsNotVertex(X) || gp_IsNotVertex(Y) || gp_IsNotVertex(W)) + return NOTOK; + + // Clear the type member of each vertex in the bicomp + if (_ClearVertexTypeInBicomp(theGraph, R) != OK) + return NOTOK; + + // Traverse from R to W in the X direction + ZPrevLink = 1; + Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); + ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RXW; + while (Z != W) + { + if (Z == X) + ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RXW; + gp_ResetVertexObstructionType(theGraph, Z, ZType); + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + // Traverse from R to W in the Y direction + ZPrevLink = 0; + Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); + ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RYW; + while (Z != W) + { + if (Z == Y) + ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RYW; + gp_ResetVertexObstructionType(theGraph, Z, ZType); + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + return OK; +} + +/**************************************************************************** + _PopAndUnmarkVerticesAndEdges() + + Pop all vertex/edge pairs from the top of the stack up to a terminating + vertex Z and mark as unvisited. If Z is NIL, then all vertex/edge pairs + are popped and marked as unvisited. + The stackBottom indicates where other material besides the vertex/edge + pairs may appear. + ****************************************************************************/ + +int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom) +{ + int V, e; + + // Pop vertex/edge pairs until all have been popped from the stack, + // and all that's left is what was under the pairs, or until... + while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) + { + sp_Pop(theGraph->theStack, V); + + // If we pop the terminating vertex Z, then put it back and break + if (V == Z) + { + sp_Push(theGraph->theStack, V); + break; + } + + // Otherwise, pop the edge part of the vertex/edge pair + sp_Pop(theGraph->theStack, e); + + // Now unmark the vertex and edge (i.e. revert to "unvisited") + gp_ClearVertexVisited(theGraph, V); + gp_ClearEdgeVisited(theGraph, e); + gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + } + + return OK; +} + +/**************************************************************************** + _MarkHighestXYPath() + + Sets the visited flags on the highest X-Y path, i.e. the one closest to the + root vertex R of the biconnected component containing X and Y as well as a + pertinent vertex W that the Walkdown could not reach due to the future + pertinent vertices X and Y being along both external face paths + emanating from the root R. + + The caller receives an OK result if the method succeeded in operating or + NOTOK on an internal failure. However, there may or may not be any X-Y path, + in which case no visitation flags are set and the return result is still OK + because the caller must decide whether the absence of an X-Y path is an + operational error. + + This method also sets the isolator context's points of attachment on the + external face of the marked X-Y path, if there was an X-Y path. So, the + caller can also use this call to decide if there was an X-Y path by + testing whether theGraph->IC.px and py have been set to non-NIL values. + ****************************************************************************/ + +int _MarkHighestXYPath(graphP theGraph) +{ + return _MarkClosestXYPath(theGraph, theGraph->IC.r); +} + +/**************************************************************************** + _MarkLowestXYPath() + + Sets the visited flags on the lowest X-Y path, i.e. the one closest to the + pertinent vertex W that the Walkdown could not reach due to future pertinent + vertices X and Y along both external face paths emanating from the root R + of the biconnected component containing W, X and Y. + + The caller receives an OK result if the method succeeded in operating or + NOTOK on an internal failure. However, there may or may not be any X-Y path, + in which case no visitation flags are set and the return result is still OK + because the caller must decide whether the absence of an X-Y path is an + operational error. + + This method also sets the isolator context's points of attachment on the + external face of the marked X-Y path, if there was an X-Y path. So, the + caller can also use this call to decide if there was an X-Y path by + testing whether theGraph->IC.px and py have been set to non-NIL values. + ****************************************************************************/ + +int _MarkLowestXYPath(graphP theGraph) +{ + return _MarkClosestXYPath(theGraph, theGraph->IC.w); +} + +/**************************************************************************** + _MarkClosestXYPath() + + This method searches for and marks the X-Y path in the bicomp rooted by R + that is either closest to R (highest) or closest to W (lowest). This method + will return NOTOK if the targetVertex parameter is other than R or W. + + The closest X-Y path is the path through the inside of the bicomp that + only attaches to the external face at its two endpoint vertices and + those endpoints are closer along the external face path emanating from + the targetVertex than the attachment points of any other X-Y path. + + This method returns NOTOK if there is an internal processing error and + otherwise it returns OK. If there is no X-Y path, the method still returns + OK to indicate no internal failures, but on return the caller can detect + whether there was an X-Y path by testing whether the attachment points in + the isolator context have been set to non-NIL values. Specifically, test + whether theGraph->IC.px and py have been set to non-NIL values. The caller + must decide whether the absence of an X-Y path is an error. For example, + in core planarity, the proof of correctness guarantees an X-Y path exists + by the time this method is called, so that caller would decide to return + NOTOK since there should be an X-Y path. + + If there is an X-Y path, it is marked using the visited flags on the + vertices and edges. + + PRECONDITION: During non-planarity context initialization, the vertices along + the external face (other than R and W) have been classified as 'high RXW', + 'low RXW', 'high RXY', or 'low RXY'. Once the vertices have been categorized, + we proceed with trying to set the visitation flags of vertices and edges. + First, we remove all edges incident to the targetVertex, except the two edges + that join it to the external face. The result is that the targetVertex and its + two remaining edges are a 'corner' in the external face but also in a single + proper face whose boundary includes the X-Y path with the closest attachment + points. Thus, we simply need to walk this proper face to find the desired + X-Y path. Note, however, that the resulting face boundary may have attached + cut vertices. Any such separable component contains a vertex neighbor of + the targetVertex, but the edge to the targetVertex has been temporarily hidden. + The algorithm removes loops of vertices and edges along the proper face so + that only the desired path is identified. + + To walk the proper face containing the targetVertex, we first identify an + arc that will be considered to be the one used to enter the targetVertex. + When the first loop iteration exits the targetVertex, it comes out on the + RXW side (even though it may be an internal vertex not marked RXW). + Then we take either the next arc (if targetVertex==W) or predecessor arc + (if targetVertex==R) at every subsequent corner to determine the exit arc + for the vertex. Then, we use the twin arc of the exit arc to determine the + entry arc for the next vertex. + + For each vertex, we mark as visited the vertex as well as both arcs of + the edge used to enter the vertex. We also push the visited vertices and + edges onto a stack. Each time the traversal lands on an external face + vertex on the RXW side, it is recorded as a candidate point of attachment Px. + We also pop and unmark all previously visited vertices and edges because they + are now known to not be part of the internal X-Y path after encountering the + point of attachment. Instead, these preceding edges and vertices were part + of a path parallel to the external face that does not obstruct the space + between R and W inside the bicomp. + + As we walk the proper face, we keep track of the last vertex P_x we visited of + type RXW (high or low). If we encounter antipodalVertex of the targetVertex + (i.e., W if the targetVertex is R, or R if the targetVertex is W), then there + is no obstructing X-Y path since we removed only edges incident to the + targetVertex, so we pop the stack unmarking everything then clear the + X-Y path points of attachment in the isolator context so the caller will know + that no X-Y path was found (and then we return OK since there was no internal + error). + + If, during the traversal, we encounter a vertex Z previously visited, then we + pop the stack, unmarking the vertices and edges popped, until we find the + prior occurence of Z on the stack. This is because we have traversed a path + that would connect back to the targetVertex had we not hidden the internal + edges of the targetVertex. + + Otherwise, the first time we encounter a vertex of type 'RYW', we stop + because the obstructing X-Y path has been marked visited and its points of + attachment to the external face have been found. This second point of + attachment Py is stored in the isolator context. + + Once the X-Y path is identified (or once we have found that there is no + X-Y path), we restore the previously hidden edges incident to the targetVertex. + + This method uses the stack, but it preserves any prior content. + The stack space used is no greater than 3N. The first N accounts for hiding + the edges incident to the targetVertex. The other 2N accounts for the fact + that each iteration of the main loop visits a vertex, pushing its index and + the location of an edge record. If a vertex is encountered that is already + on the stack, then it is not pushed again (and in fact part of the stack + is removed). + + Returns OK on successful operation and NOTOK on internal failure. + Also, if an X-Y path is found (which will be the closest), then + the graph isolator context contains its attachment points on the + external face of the bicomp rooted by R, and the edges and vertices + in the X-Y path have been marked visted. + ****************************************************************************/ + +int _MarkClosestXYPath(graphP theGraph, int targetVertex) +{ + int e, Z; + int R, W, antipodalVertex; + int stackBottom1, stackBottom2; + + /* Initialization */ + + R = theGraph->IC.r; + W = theGraph->IC.w; + theGraph->IC.px = theGraph->IC.py = NIL; + + /* This method only makes sense for a targetVertex of R or W */ + if (targetVertex != R && targetVertex != W) + return NOTOK; + + /* The vertex opposite the targetVertex is needed to detect when + there is no X-Y path */ + + antipodalVertex = targetVertex == R ? W : R; + + /* Save the stack bottom before we start hiding internal edges, so + we will know how many edges to restore */ + + stackBottom1 = sp_GetCurrentSize(theGraph->theStack); + + /* Remove the internal edges incident to targetVertex (R or W) */ + + if (_HideInternalEdges(theGraph, targetVertex) != OK) + return NOTOK; + + /* Now we're going to use the stack to collect the vertices of potential + * X-Y paths, so we need to store where the hidden internal edges are + * located because we must, at times, pop the collected vertices if + * the path being collected doesn't work out. */ + + stackBottom2 = sp_GetCurrentSize(theGraph->theStack); + + /* Walk the proper face containing targetVertex to find and mark the + closest X-Y path. */ + + Z = targetVertex; + + // Now we will get the arc that we consider to be the arc used to enter + // the targetVertex (which will be an edge on the RYW side, and the + // first line of the loop code will get the previous or next arc to exit + // the targetVertex on the RXW side of the bicomp) + e = targetVertex == R ? gp_GetLastArc(theGraph, R) : gp_GetFirstArc(theGraph, W); + + while (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_HIGH_RYW && + gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_LOW_RYW) + { + /* Advance e and Z along the proper face containing the targetVertex */ + + // Get the opposing arc of the corner at vertex Z, as the arc to exit Z + e = targetVertex == R ? gp_GetPrevArcCircular(theGraph, e) + : gp_GetNextArcCircular(theGraph, e); + + // Now use the exit arc to get the next Z to visit + Z = gp_GetNeighbor(theGraph, e); + + // And get the entry arc of the new Z being visited + e = gp_GetTwinArc(theGraph, e); + + /* If Z is already visited, then pop everything since the last time + we visited Z because its all part of a separable component. */ + + if (gp_GetVertexVisited(theGraph, Z)) + { + if (_PopAndUnmarkVerticesAndEdges(theGraph, Z, stackBottom2) != OK) + return NOTOK; + } + + /* If we have not visited this vertex before... */ + + else + { + /* If we find the antipodalVertex of the targetVertex (W if R, R if W), + then there is no X-Y path. Never happens for Kuratowski subgraph + isolator, but this routine is also used to test for certain X-Y paths. + So, we clean up and bail out in that case. */ + + if (Z == antipodalVertex) + { + if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK) + return NOTOK; + break; + } + + /* If we found another vertex along the RXW path, then blow off + all the vertices we visited so far because they're not part of + the obstructing path */ + + if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW || + gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) + { + theGraph->IC.px = Z; + if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK) + return NOTOK; + } + + /* Push the current vertex onto the stack of vertices visited + since the last RXW vertex was encountered */ + + sp_Push(theGraph->theStack, e); + sp_Push(theGraph->theStack, Z); + + /* Mark the vertex Z as visited as well as its edge of entry + (except the entry edge for P_x).*/ + + gp_SetVertexVisited(theGraph, Z); + if (Z != theGraph->IC.px) + { + gp_SetEdgeVisited(theGraph, e); + gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); + } + + /* If we found an RYW vertex, then we have successfully finished + identifying the closest X-Y path, so we record the point of + attachment and break the loop. */ + + if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW || + gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) + { + theGraph->IC.py = Z; + break; + } + } + } + + /* Remove any remaining vertex-edge pairs on the top of the stack, then + Restore the internal edges incident to R that were previously removed. */ + + sp_SetCurrentSize(theGraph->theStack, stackBottom2); + + if (_RestoreInternalEdges(theGraph, stackBottom1) != OK) + return NOTOK; + + /* Return the result */ + + if (!gp_IsVertex(theGraph->IC.py)) + theGraph->IC.px = NIL; + + return OK; +} + +/**************************************************************************** + _MarkZtoRPath() + + This function assumes that _MarkHighestXYPath() has already been called, + which marked as visited the vertices and edges along the X-Y path. + + We begin at the point of attachment P_x, take the last arc and traverse + the predecessor arcs until we find one marked visited, which leads to the + first internal vertex along the X-Y path. We begin with this vertex + (and its edge of entry), and we run until we find P_y. For each internal + vertex Z and its edge of entry ZPrevArc, we take the predecessor edge record + of ZPrevArc. This is called ZNextArc. If ZNextArc is marked visited + then it is along the X-Y path, so we use it to exit Z and go to the next + vertex on the X-Y path. + + If ZNextArc is not visited, then when _MarkHighestXYPath() ran, it exited + Z from ZNextArc, then eventually reentered Z. In other words, Z became a + cut vertex when we removed the internal edges incident to R. Thus, ZNextArc + indicates the first edge in an internal path to R. + + When we find an unvisited ZNextArc, we stop running the X-Y path and instead + begin marking the Z to R path. We move to successive vertices using a + twin arc then its predecessor arc in the adjacency list, only this time + we have not removed the internal edges incident to R, so this technique does + eventually lead us all the way to R. + + If we do not find an unvisited ZNextArc for any vertex Z on the X-Y path and + inside the bicomp, then there is no Z to R path, so we return. + ****************************************************************************/ + +int _MarkZtoRPath(graphP theGraph) +{ + int ZPrevArc, ZNextArc, Z, R, Px, Py; + + /* Initialize */ + + R = theGraph->IC.r; + Px = theGraph->IC.px; + Py = theGraph->IC.py; + theGraph->IC.z = NIL; + + /* Begin at Px and search its adjacency list for the edge leading to + the first internal vertex of the X-Y path. */ + + Z = Px; + ZNextArc = gp_GetLastArc(theGraph, Z); + while (ZNextArc != gp_GetFirstArc(theGraph, Z)) + { + if (gp_GetEdgeVisited(theGraph, ZNextArc)) + break; + + ZNextArc = gp_GetPrevArc(theGraph, ZNextArc); + } + + if (!gp_GetEdgeVisited(theGraph, ZNextArc)) + return NOTOK; + + /* For each internal vertex Z, determine whether it has a path to root. */ + + while (gp_GetEdgeVisited(theGraph, ZNextArc)) + { + ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); + ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc); + } + + ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); + Z = gp_GetNeighbor(theGraph, ZPrevArc); + + /* If there is no Z to R path, return */ + + if (Z == Py) + return OK; + + /* Otherwise, store Z in the isolation context */ + + theGraph->IC.z = Z; + + /* Walk the proper face starting with (Z, ZNextArc) until we reach R, marking + the vertices and edges encountered along the way, then Return OK. */ + + while (Z != R) + { + /* If we ever encounter a non-internal vertex (other than the root R), + then corruption has occured, so we return NOTOK */ + + if (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_UNKNOWN) + return NOTOK; + + /* Go to the next vertex indicated by ZNextArc */ + + Z = gp_GetNeighbor(theGraph, ZNextArc); + + /* Mark the next vertex and the edge leading to it as visited. */ + + gp_SetEdgeVisited(theGraph, ZNextArc); + gp_SetEdgeVisited(theGraph, ZPrevArc); + gp_SetVertexVisited(theGraph, Z); + + /* Go to the next edge in the proper face */ + + ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc); + ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); + } + + /* Found Z to R path, so indicate as much to caller */ + + return OK; +} + +/**************************************************************************** + _FindFuturePertinenceBelowXYPath() + + Get a future pertinent vertex along the lower external face path between + the points of attachment P_x and P_y of a 'low' X-Y Path. + NOTE: By the time this function is called, Px and Py have already been found + to be at or below X and Y. + ****************************************************************************/ + +int _FindFuturePertinenceBelowXYPath(graphP theGraph) +{ + int Z = theGraph->IC.px, ZPrevLink = 1, + Py = theGraph->IC.py, v = theGraph->IC.v; + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + + while (Z != Py) + { + gp_UpdateVertexFuturePertinentChild(theGraph, Z, v); + if (FUTUREPERTINENT(theGraph, Z, v)) + return Z; + + Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); + } + + return NIL; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphOuterplanarObstruction.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphOuterplanarObstruction.c new file mode 100644 index 000000000..0f4b4e63b --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphOuterplanarObstruction.c @@ -0,0 +1,196 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "../graph.h" + +/* Imported functions */ + +extern void _ClearVisitedFlags(graphP); + +extern int _JoinBicomps(graphP theGraph); + +extern int _InitializeNonplanarityContext(graphP theGraph, int v, int R); +extern int _MarkHighestXYPath(graphP theGraph); + +// extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); +extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); +extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); + +extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); + +extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); + +extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); + +/* Private function declarations (exported to system) */ + +int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R); + +int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); + +int _IsolateOuterplanarityObstructionA(graphP theGraph); +int _IsolateOuterplanarityObstructionB(graphP theGraph); +int _IsolateOuterplanarityObstructionE(graphP theGraph); + +/**************************************************************************** + _ChooseTypeOfNonOuterplanarityMinor() + A constant time implementation is easily feasible but only constant amortized + time is needed for the outerplanarity obstruction isolation, which also + benefits from having the bicomp rooted by R oriented. + If an extension algorithm requires constant actual time, then this function + should not be used and instead the minor should be decided without orienting + the bicomp. + ****************************************************************************/ + +int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R) +{ + int W; + + // Create the initial non-outerplanarity obstruction isolator state. + if (_InitializeNonplanarityContext(theGraph, v, R) != OK) + return NOTOK; + + R = theGraph->IC.r; + W = theGraph->IC.w; + + // If the root copy is not a root copy of the current vertex v, + // then the Walkdown terminated on a descendant bicomp, which is Minor A. + if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) + { + theGraph->IC.minorType |= MINORTYPE_A; + return OK; + } + + // If W has a pertinent child bicomp, then we've found Minor B. + // Notice this is different from planarity, in which minor B is indicated + // only if the pertinent child bicomp is also future pertinent. + if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) + { + theGraph->IC.minorType |= MINORTYPE_B; + return OK; + } + + // The only other result is minor E (we will search for the X-Y path later) + theGraph->IC.minorType |= MINORTYPE_E; + return OK; +} + +/**************************************************************************** + _IsolateOuterplanarObstruction() + ****************************************************************************/ + +int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R) +{ + int RetVal; + + /* A subgraph homeomorphic to K_{2,3} or K_4 will be isolated by using the visited + flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we + subsequently set visited on all edges and vertices in the homeomorph. */ + + _ClearVisitedFlags(theGraph); + + /* Next we determineg which of the non-outerplanarity Minors was encountered + and the principal bicomp on which the isolator will focus attention. */ + + if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) + return NOTOK; + + /* Find the path connecting the pertinent vertex w with the current vertex v */ + + if (theGraph->IC.minorType & MINORTYPE_B) + { + isolatorContextP IC = &theGraph->IC; + int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); + + if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE) + return NOTOK; + } + else + { + isolatorContextP IC = &theGraph->IC; + + if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) + return NOTOK; + } + + /* For minor E, we need to find and mark an X-Y path */ + + if (theGraph->IC.minorType & MINORTYPE_E) + { + if (_MarkHighestXYPath(theGraph) != OK || theGraph->IC.py == NIL) + return NOTOK; + } + + /* Call the appropriate isolator */ + + if (theGraph->IC.minorType & MINORTYPE_A) + RetVal = _IsolateOuterplanarityObstructionA(theGraph); + else if (theGraph->IC.minorType & MINORTYPE_B) + RetVal = _IsolateOuterplanarityObstructionB(theGraph); + else if (theGraph->IC.minorType & MINORTYPE_E) + RetVal = _IsolateOuterplanarityObstructionE(theGraph); + else + RetVal = NOTOK; + + /* Delete the unmarked edges and vertices, and return */ + + if (RetVal == OK) + RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph); + + return RetVal; +} + +/**************************************************************************** + _IsolateOuterplanarityObstructionA(): Isolate a K2,3 homeomorph + ****************************************************************************/ + +int _IsolateOuterplanarityObstructionA(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, IC->v, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateOuterplanarityObstructionB(): Isolate a K2,3 homeomorph + ****************************************************************************/ + +int _IsolateOuterplanarityObstructionB(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) + return NOTOK; + + return OK; +} + +/**************************************************************************** + _IsolateOuterplanarityObstructionE(): Isolate a K4 homeomorph + ****************************************************************************/ + +int _IsolateOuterplanarityObstructionE(graphP theGraph) +{ + isolatorContextP IC = &theGraph->IC; + + if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || + theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || + _JoinBicomps(theGraph) != OK || + _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) + return NOTOK; + + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c new file mode 100644 index 000000000..36920391b --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c @@ -0,0 +1,968 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#define GRAPHTEST_C + +#include "../graph.h" +#include "../lowLevelUtils/stack.h" + +extern void _ClearVertexVisitedFlags(graphP theGraph, int); + +/* Private function declarations */ + +int _TestPath(graphP theGraph, int U, int V); +int _TryPath(graphP theGraph, int e, int V); +void _MarkPath(graphP theGraph, int e); +int _TestSubgraph(graphP theSubgraph, graphP theGraph); + +int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); +int _CheckEmbeddingFacialIntegrity(graphP theGraph); +int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph); + +int _CheckKuratowskiSubgraphIntegrity(graphP theGraph); +int _CheckOuterplanarObstructionIntegrity(graphP theGraph); + +int _CheckAllVerticesOnExternalFace(graphP theGraph); +void _MarkExternalFaceVertices(graphP theGraph, int startVertex); + +/******************************************************************** + gp_TestEmbedResultIntegrity() + + This function tests the integrity of the graph result returned + from gp_Embed(). + + The caller of gp_Embed() does not have to save the original graph + because, for efficiency, gp_Embed() operates on the input graph. + However, to test the integrity of the result relative to the input, + a copy of the input graph is required. + + Modules that extend/alter the behavior of gp_Embed() beyond the + core planarity embedder and planarity obstruction isolator should + also provide overriding integrity test routines appropriate to the + extension algorithm. + + The main method first calls gp_SortVertices on theGraph, if the + origGraph is not in DFI order (the common case). Therefore, + extension integrity tests can count on a consistent numbering + between theGraph and the origGraph, either DFI order or pre-DFS + order if that is the state of the origGraph. + + After all tests, the main method ensures theGraph is restored to + DFI order by invoking gp_SortVertices if needed, thus ensuring + that theGraph has the documented post-condition of gp_Embed(). + + For an embedResult of OK, fpCheckEmbeddingIntegrity is invoked. + The core planarity implementation does a face walk of all faces + of the embedding. It ensures that all edges were used in the face + walk and that the right number of faces exist for the number of + vertices and edges. Also, we ensure that all adjacencies expressed + in the original graph still exist in the result graph. + + For an embedResult of NONEMBEDDABLE, fpCheckObstructionIntegrity + is invoked. The core planarity algorithm checks that the result + graph is homeomorphic to K5 or K3,3 and that it is in fact a + subgraph of the input graph. Other algorithms use overloads to + make appropriate checks. + + Returns NOTOK on integrity check failure or embedResult of NOTOK + OK for successful integrity check of OK embedResult + NONEMBEDDABLE for successful integrity check of an + embedResult of NONEMBEDDABLE + ********************************************************************/ + +int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult) +{ + int RetVal = embedResult; + + if (theGraph == NULL || origGraph == NULL) + return NOTOK; + + if (embedResult == OK) + { + RetVal = theGraph->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); + } + else if (embedResult == NONEMBEDDABLE) + { + RetVal = theGraph->functions.fpCheckObstructionIntegrity(theGraph, origGraph); + } + + if (RetVal == OK) + RetVal = embedResult; + + return RetVal; +} + +/******************************************************************** + _CheckEmbeddingIntegrity() + + The core planarity implementation does a face walk of all faces + of the embedding. It ensures that all edges were used in the face + walk and that the right number of faces exist for the number of + vertices and edges. Also, we ensure that all adjacencies expressed + in the original graph still exist in the result graph, accounting + for the fact that the result graph is sorted by DFI, but the input + may or may not be sorted by DFI. + + returns OK if all integrity tests passed, NOTOK otherwise + ********************************************************************/ + +int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) +{ + if (theGraph == NULL || origGraph == NULL) + return NOTOK; + + if (_TestSubgraph(theGraph, origGraph) != TRUE) + return NOTOK; + + if (_TestSubgraph(origGraph, theGraph) != TRUE) + return NOTOK; + + if (_CheckEmbeddingFacialIntegrity(theGraph) != OK) + return NOTOK; + + if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR) + { + if (_CheckAllVerticesOnExternalFace(theGraph) != OK) + return NOTOK; + } + + return OK; +} + +/******************************************************************** + _CheckEmbeddingFacialIntegrity() + + This function traverses all faces of a graph structure containing + the planar embedding that results from gp_Embed(). The algorithm + begins by placing all of the graph's arcs onto a stack and marking + all of them as unvisited. For each arc popped, if it is visited, + it is immediately discarded and the next arc is popped. Popping an + unvisited arc e begins a face traversal. We move to the true twin + arc of e, and obtain its successor arc. This amounts to always + going clockwise or counterclockwise (depending on how the graph is + drawn on the plane, or alternately whether one is above or below + the plane). This traversal continues until we make it back to the + original arc e. Each arc along the way is marked as visited. Further, + if the successor arc has been visited, then there is an error since + an arc can only appear in one face (the twin arc appears in a separate + face, which is traversed in the opposing direction). + If this algorithm succeeds without double visiting any arcs, and it + produces the correct face count according to Euler's formula, then + the embedding has all vertices oriented the same way. + NOTE: In disconnected graphs, the face reader counts the external + face of each connected component. So, we adjust the face + count by subtracting one for each component, then we add one + to count the external face shared by all components. + ********************************************************************/ + +int _CheckEmbeddingFacialIntegrity(graphP theGraph) +{ + stackP theStack = theGraph->theStack; + int EsizeOccupied, v, e, eTwin, eStart, eNext, NumFaces, connectedComponents; + + if (theGraph == NULL) + return NOTOK; + + /* The stack need only contain 2M entries, one for each edge record. With + max M at 3N, this amounts to 6N integers of space. The embedding + structure already contains this stack, so we just make sure it + starts out empty. */ + + sp_ClearStack(theStack); + + /* Push all arcs and set them to unvisited */ + + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e += 2) + { + // Except skip edge holes + if (gp_EdgeInUse(theGraph, e)) + { + sp_Push(theStack, e); + gp_ClearEdgeVisited(theGraph, e); + eTwin = gp_GetTwinArc(theGraph, e); + sp_Push(theStack, eTwin); + gp_ClearEdgeVisited(theGraph, eTwin); + } + } + + // There are M edges, so we better have pushed 2M arcs just now + // i.e. testing that the continue above skipped only edge holes + if (sp_GetCurrentSize(theStack) != 2 * theGraph->M) + return NOTOK; + + /* Read faces until every arc is used */ + + NumFaces = 0; + while (sp_NonEmpty(theStack)) + { + /* Get an arc; if it has already been used by a face, then + don't use it to traverse a new face */ + sp_Pop(theStack, eStart); + if (gp_GetEdgeVisited(theGraph, eStart)) + continue; + + e = eStart; + do + { + eNext = gp_GetNextArcCircular(theGraph, gp_GetTwinArc(theGraph, e)); + if (gp_GetEdgeVisited(theGraph, eNext)) + return NOTOK; + gp_SetEdgeVisited(theGraph, eNext); + e = eNext; + } while (e != eStart); + NumFaces++; + } + + /* Count the external face once rather than once per connected component; + each connected component is detected by the fact that it has no + DFS parent, except in the case of isolated vertices, no face was counted + so we do not subtract one. */ + + connectedComponents = 0; + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (gp_IsDFSTreeRoot(theGraph, v)) + { + if (gp_GetVertexDegree(theGraph, v) > 0) + NumFaces--; + connectedComponents++; + } + } + + NumFaces++; + + /* Test number of faces using the extended Euler's formula. + For connected components, Euler's formula is f=m-n+2, but + for disconnected graphs it is extended to f=m-n+1+c where + c is the number of connected components.*/ + + return NumFaces == theGraph->M - theGraph->N + 1 + connectedComponents + ? OK + : NOTOK; +} + +/******************************************************************** + _CheckAllVerticesOnExternalFace() + + Determines whether or not any vertices have been embedded within + the bounding cycle of the external face. + The input graph may be disconnected, so this routine walks the + external face starting at each vertex with no DFSParent. + + return OK if all vertices visited on external face walks, NOTOK otherwise + ********************************************************************/ + +int _CheckAllVerticesOnExternalFace(graphP theGraph) +{ + int v; + + // Mark all vertices unvisited + _ClearVertexVisitedFlags(theGraph, FALSE); + + // For each connected component, walk its external face and + // mark the vertices as visited + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + if (gp_IsDFSTreeRoot(theGraph, v)) + _MarkExternalFaceVertices(theGraph, v); + } + + // If any vertex is unvisited, then the embedding is not an outerplanar + // embedding, so we return NOTOK + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (!gp_GetVertexVisited(theGraph, v)) + return NOTOK; + + // All vertices were found on external faces of the connected components + // so the embedding is an outerplanar embedding and we return OK + return OK; +} + +/******************************************************************** + _MarkExternalFaceVertices() + + Walks the external face of the connected component containing the + start vertex, and marks the visited flag of all vertices found. + The start vertex is assumed to be on the external face. + This method assumed the embedding integrity has already been + verified to be correct. + This method correctly handles components that have cut vertices, + i.e. it does not assume that the outer face is a simple cycle; + it only assumes that all vertices are reachable by walking a + single face that starts with startVertex. + ********************************************************************/ + +void _MarkExternalFaceVertices(graphP theGraph, int startVertex) +{ + int nextVertex = startVertex; + int e = gp_GetFirstArc(theGraph, nextVertex); + int eTwin; + + // Handle the case of an isolated vertex + if (gp_IsNotArc(e)) + { + gp_SetVertexVisited(theGraph, startVertex); + return; + } + + // Process a non-trivial connected component + do + { + gp_SetVertexVisited(theGraph, nextVertex); + + // The arc out of the vertex just visited points to the next vertex + nextVertex = gp_GetNeighbor(theGraph, e); + + // Arc used to enter the next vertex is needed so we can get the + // next edge in rotation order. + // Note: for bicomps, first and last arcs of all external face vertices + // indicate the edges that hold them to the external face + // But _JoinBicomps() has already occurred, so cut vertices + // will have external face edges other than the first and last arcs + // Hence we need this more sophisticated traversal method + eTwin = gp_GetTwinArc(theGraph, e); + + // Now we get the next arc in rotation order as the new arc out to the + // vertex after nextVertex. This sets us up for the next iteration. + // Note: We cannot simply follow the chain of nextVertex first arcs + // as we started out doing at the top of this method. This is + // because we are no longer dealing with bicomps only. + // Since _JoinBicomps() has already been invoked, there may now + // be cut vertices on the external face whose adjacency lists + // contain external face arcs in positions other than the first and + // and last arcs. We will visit those vertices multiple times, + // which is OK (just that we have to explain why we're calculating + // jout in this way). + e = gp_GetNextArcCircular(theGraph, eTwin); + + // Now things get really interesting. The DFS root (startVertex) may + // itself be a cut vertex to which multiple bicomps have been joined. + // So we cannot simply stop when the external face walk gets back to + // startVertex. We must actually get back to startVertex using its + // last arc. This ensures that we've looped down into all the DFS + // subtrees rooted at startVertex and walked their external faces. + + // Since we started the whole external face walk with the first arc + // of startVertex, we need to proceed until we reenter startVertex + // using its last arc. + + } while (eTwin != gp_GetLastArc(theGraph, startVertex)); +} + +/******************************************************************** + _CheckObstructionIntegrity() + + Returns OK if theGraph is a subgraph of origGraph and it contains + an allowed homeomorph, and NOTOK otherwise. + + For core planarity, the allowed homeomorphs are K_5 or K_{3,3} + + Extension modules may overload this method to implement different + tests. For example, K_{3,3} search allows only K_{3,3} and + outerplanarity allows only K_4 or K_{2,3}. + ********************************************************************/ + +int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph) +{ + if (theGraph == NULL || origGraph == NULL) + return NOTOK; + + if (_TestSubgraph(theGraph, origGraph) != TRUE) + { + return NOTOK; + } + + if (theGraph->embedFlags == EMBEDFLAGS_PLANAR) + return _CheckKuratowskiSubgraphIntegrity(theGraph); + + else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR) + return _CheckOuterplanarObstructionIntegrity(theGraph); + + return NOTOK; +} + +/******************************************************************** + _getImageVertices() + + Count the number of vertices of each degree and find the locations of + the image vertices (also sometimes called the corners of the obstruction). + An image vertex is a vertex of degree three or higher because degree + 2 vertices are generally internal to the paths between the image + vertices. + + The notable exception is K_{2,3}, an obstruction to outerplanarity. + This routine does not know the obstruction it is looking for, so the + caller must decide whether there are any degree 2 vertices that should + be added to imageVerts. + + Return NOTOK if any vertex of degree 1 or higher than the max is found + NOTOK if more than the max number of image vertices is found. + Return OK otherwise. + ********************************************************************/ + +int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, + int *imageVerts, int maxNumImageVerts) +{ + int K, v, imageVertPos, degree; + + for (degree = 0; degree <= maxDegree; degree++) + degrees[degree] = 0; + + for (K = 0; K < maxNumImageVerts; K++) + imageVerts[K] = NIL; + + imageVertPos = 0; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + { + degree = gp_GetVertexDegree(theGraph, v); + if (degree == 1) + return NOTOK; + if (degree > maxDegree) + return NOTOK; + + degrees[degree]++; + + if (imageVertPos < maxNumImageVerts && degree > 2) + imageVerts[imageVertPos++] = v; + else if (degree > 2) + return NOTOK; + } + + return OK; +} + +/******************************************************************** + _TestForCompleteGraphObstruction() + + theGraph - the graph to test + numVerts - the number of image vertices (corners) of the complete + graph homeomorph being tested for (e.g. 5 for a K5) + degrees - array of counts of the number of vertices of each degree + given by the array index. Only valid up to numVerts-1 + imageVerts - the vertices of degree numVerts-1 + + This routine tests whether theGraph is a K_{numVerts} homeomorph for + numVerts >= 4. + + returns FALSE if numVerts < 4, + if theGraph has other than numVerts image vertices + if theGraph contains other than degree 2 vertices plus + the image vertices + if any pair of image vertices lacks a connecting path + if any degree two vertices are not in the connecting paths + TRUE otherwise + ********************************************************************/ + +int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, + int *degrees, int *imageVerts) +{ + int v, w; + + // We need to make sure we have numVerts vertices of degree numVerts-1 + // For example, if numVerts==5, then we're looking for a K5, so we + // need to have degrees[4] == 5 (5 vertices of degree 4) + if (degrees[numVerts - 1] != numVerts) + return FALSE; + + // All vertices need to be degree 0, degree 2 or degree numVerts-1 + if (degrees[0] + degrees[2] + degrees[numVerts - 1] != theGraph->N) + return FALSE; + + // We clear all the vertex visited flags + _ClearVertexVisitedFlags(theGraph, FALSE); + + // For each pair of image vertices, we test that there is a path + // between the two vertices. If so, the visited flags of the + // internal vertices along the path are marked + // + for (v = 0; v < numVerts; v++) + for (w = 0; w < numVerts; w++) + if (v != w) + if (_TestPath(theGraph, imageVerts[v], + imageVerts[w]) != TRUE) + return FALSE; + + // The visited flags should have marked only degree two vertices, + // so for every marked vertex, we subtract one from the count of + // the degree two vertices. + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (gp_GetVertexVisited(theGraph, v)) + degrees[2]--; + + /* If every degree 2 vertex is used in a path between image + vertices, then there are no extra pieces of the graph + in theGraph. Specifically, the prior tests identify + a K_5 and ensure that nothing else could exist in the + graph except extra degree 2 vertices, which must be + joined in a cycle so that all are degree 2. */ + + return degrees[2] == 0 ? TRUE : FALSE; +} + +/******************************************************************** + _TestForK33GraphObstruction() + + theGraph - the graph to test + degrees - array of counts of the number of vertices of each degree + given by the array index. Only valid up to numVerts-1 + imageVerts - the degree 3 vertices of the K3,3 homeomorph + + This routine tests whether theGraph is a K_{3,3} homeomorph. + + returns TRUE if so, FALSE if not + ********************************************************************/ + +int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) +{ + int v, K, imageVertPos, temp, success; + + if (degrees[4] != 0) + return FALSE; + + if (degrees[3] != 6) + return FALSE; + + /* Partition the six image vertices into two sets of 3 + (or report failure) */ + + for (imageVertPos = 3; imageVertPos < 6; imageVertPos++) + { + K = 0; + success = FALSE; + do + { + if (_TestPath(theGraph, imageVerts[imageVertPos], imageVerts[0]) == TRUE) + { + success = TRUE; + break; + } + + K++; + temp = imageVerts[K]; + imageVerts[K] = imageVerts[imageVertPos]; + imageVerts[imageVertPos] = temp; + } while (K < 3); + + if (!success) + return FALSE; + } + + /* Now test the paths between each of the first three vertices and + each of the last three vertices */ + + _ClearVertexVisitedFlags(theGraph, FALSE); + + for (imageVertPos = 0; imageVertPos < 3; imageVertPos++) + for (K = 3; K < 6; K++) + if (_TestPath(theGraph, imageVerts[imageVertPos], + imageVerts[K]) != TRUE) + return FALSE; + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (gp_GetVertexVisited(theGraph, v)) + degrees[2]--; + + /* If every degree 2 vertex is used in a path between image + vertices, then there are no extra pieces of the graph + in theGraph. Specifically, the prior tests identify + a K_{3,3} and ensure that nothing else could exist in the + graph except extra degree 2 vertices, which must be + joined in a cycle so that all are degree 2. */ + + return degrees[2] == 0 ? TRUE : FALSE; +} + +/******************************************************************** + _CheckKuratowskiSubgraphIntegrity() + + This function checks whether theGraph received as input contains + either a K_5 or K_{3,3} homeomorph. + + RETURNS: OK if theGraph contains a K_5 or K_{3,3} homeomorph, + NOTOK otherwise + + To be a K_5 homeomorph, there must be exactly 5 vertices of degree 4, + which are called 'image' vertices, and all other vertices must be + either degree 2 or degree 0. Furthermore, each of the image vertices + must be able to reach all of the other image vertices by a single edge + or a path of degree two vertices. + + To be a K_{3,3} homeomorph, there must be exactly 6 vertices of degree 3, + which are called 'image' vertices, and all other vertices must be either + degree 2 or degree 0. Furthermore, the image vertices must be connected + by edges or paths of degree two vertices in the manner suggested by + a K_{3,3}. To test this, we select an image vertex U, and determine + three image vertices X, Y and Z reachable from U by single edges or + paths of degree 2 vertices. Then, we check that the two remaining image + vertices, V and W, can also reach X, Y and Z by single edges or paths of + degree 2 vertices. + + It is not necessary to check that the paths between the image vertices + are distinct since if the paths had a common vertex, then the common + vertex would not be degree 2. + ********************************************************************/ + +int _CheckKuratowskiSubgraphIntegrity(graphP theGraph) +{ + int degrees[5], imageVerts[6]; + + if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK) + return NOTOK; + + if (_TestForCompleteGraphObstruction(theGraph, 5, degrees, imageVerts) == TRUE) + { + return OK; + } + + if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE) + { + return OK; + } + + return NOTOK; +} + +/******************************************************************** + _TestForK23GraphObstruction() + + theGraph - the graph to test + degrees - array of counts of the number of vertices of each degree + given by the array index. Only valid up to numVerts-1 + imageVerts - the degree 3 vertices of the K2,3 homeomorph + + This routine tests whether theGraph is a K_{2,3} homeomorph. + This routine operates over the results of _getImageVertices() + + returns TRUE if so, FALSE if not + ********************************************************************/ + +int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) +{ + int v, e, imageVertPos; + + // This function operates over the imageVerts results produced by + // getImageVertices, which only finds vertices of degree 3 or higher. + // So, for a K2,3, there must be exactly two degree 3 vertices and + // no degree 4 vertices. + if (degrees[3] != 2) + return FALSE; + + // For K_{2,3}, the three vertices of degree 2 were not + // detected as image vertices because degree 2 vertices + // are indistinguishable from the internal path vertices + // between the image vertices. So, here we acknowledge + // that more image vertices need to be selected. + imageVertPos = 2; + + // Assign the remaining three image vertices to be the + // neighbors of the first degree 3 image vertex. + // Ensure that each is distinct from the second + // degree 3 image vertex. This must be the case because + // the two degree 3 image vertices are in the same partition + // and hence must not be adjacent. + + e = gp_GetFirstArc(theGraph, imageVerts[0]); + while (gp_IsArc(e)) + { + imageVerts[imageVertPos] = gp_GetNeighbor(theGraph, e); + if (imageVerts[imageVertPos] == imageVerts[1]) + return FALSE; + imageVertPos++; + e = gp_GetNextArc(theGraph, e); + } + + /* The paths from imageVerts[0] to each of the new degree 2 + image vertices are the edges we just traversed. + Now test the paths between each of the degree 2 image + vertices and imageVerts[1]. */ + + _ClearVertexVisitedFlags(theGraph, FALSE); + + for (imageVertPos = 2; imageVertPos < 5; imageVertPos++) + { + if (_TestPath(theGraph, imageVerts[imageVertPos], + imageVerts[1]) != TRUE) + return FALSE; + + gp_SetVertexVisited(theGraph, imageVerts[imageVertPos]); + } + + for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) + if (gp_GetVertexVisited(theGraph, v)) + degrees[2]--; + + /* If every degree 2 vertex is used in a path between the + two degree 3 image vertices, then there are no extra + pieces of the graph in theGraph. Specifically, the + prior tests identify a K_{2,3} and ensure that nothing + else could exist in the graph... except extra degree 2 + vertices joined in a cycle. We return NOTOK in that case. */ + + return degrees[2] == 0 ? TRUE : FALSE; +} + +/******************************************************************** + _CheckOuterplanarObstructionIntegrity() + + This function checks whether theGraph received as input contains + either a K_4 or K_{2,3} homeomorph. + + RETURNS: OK if theGraph contains a K_4 or K_{2,3} homeomorph, + NOTOK otherwise + + To be a K_4 homeomorph, there must be exactly 4 vertices of degree 3, + which are called 'image' vertices, and all other vertices must be + either degree 2 or degree 0. Furthermore, each of the image vertices + must be able to reach all of the other image vertices by a single edge + or a path of degree two vertices. + + To be a K_{2,3} homeomorph, there must be exactly 2 vertices of degree 3. + All other vertices must be degree 2. Furthermore, the two degree 3 + vertices must have three internally disjoint paths connecting them, + and each path must contain at least two edges (i.e. at least one internal + vertex). The two degree 3 vertices are image vertices, and an internal + vertex from each of the three paths contributes the remaining three + image vertices. + + It is not necessary to check that the paths between the degree three + vertices are distinct since if the paths had a common vertex, then the + common vertex would not be degree 2. + ********************************************************************/ + +int _CheckOuterplanarObstructionIntegrity(graphP theGraph) +{ + int degrees[4], imageVerts[5]; + + if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK) + return NOTOK; + + if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE) + { + return OK; + } + + if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE) + { + return OK; + } + + /* We get here only if we failed to recognize an outerplanarity + obstruction, so we return failure */ + + return NOTOK; +} + +/******************************************************************** + _TestPath() + + This function determines whether there exists a path of degree two + vertices between two given vertices. The function marks each + degree two vertex as visited. It returns TRUE if it finds the + path and FALSE otherwise. + ********************************************************************/ + +int _TestPath(graphP theGraph, int U, int V) +{ + int e = gp_GetFirstArc(theGraph, U); + + while (gp_IsArc(e)) + { + if (_TryPath(theGraph, e, V) == OK) + { + _MarkPath(theGraph, e); + return TRUE; + } + + e = gp_GetNextArc(theGraph, e); + } + + return FALSE; +} + +/******************************************************************** + _TryPath() + + This function seeks a given path to a vertex V starting with a + given edge out of a starting vertex U. The path is allowed to + contain zero or more degree two vertices, but we stop as soon as + a vertex of degree higher than two is encountered. + The function returns boolean true if that vertex is V, and + boolean false otherwise. + ********************************************************************/ + +int _TryPath(graphP theGraph, int e, int V) +{ + int eTwin, nextVertex; + + nextVertex = gp_GetNeighbor(theGraph, e); + + // while nextVertex is strictly degree 2 + while (gp_IsArc(gp_GetFirstArc(theGraph, nextVertex)) && + gp_IsArc(gp_GetLastArc(theGraph, nextVertex)) && + gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex)) + { + eTwin = gp_GetTwinArc(theGraph, e); + e = gp_GetFirstArc(theGraph, nextVertex); + if (e == eTwin) + e = gp_GetLastArc(theGraph, nextVertex); + + nextVertex = gp_GetNeighbor(theGraph, e); + } + + return nextVertex == V ? TRUE : FALSE; +} + +/******************************************************************** + _MarkPath() + + This function sets the visitation flag on all degree two vertices + along a path to a vertex V that starts with a given edge out of + a starting vertex U. + ********************************************************************/ + +void _MarkPath(graphP theGraph, int e) +{ + int eTwin, nextVertex; + + nextVertex = gp_GetNeighbor(theGraph, e); + // while nextVertex is strictly degree 2 + while (gp_IsArc(gp_GetFirstArc(theGraph, nextVertex)) && + gp_IsArc(gp_GetLastArc(theGraph, nextVertex)) && + gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex)) + { + gp_SetVertexVisited(theGraph, nextVertex); + + eTwin = gp_GetTwinArc(theGraph, e); + e = gp_GetFirstArc(theGraph, nextVertex); + if (e == eTwin) + e = gp_GetLastArc(theGraph, nextVertex); + + nextVertex = gp_GetNeighbor(theGraph, e); + } +} + +/******************************************************************** + _TestSubgraph() + Checks whether theSubgraph is in fact a subgraph of theGraph. + For each vertex v in graph G and subgraph H, we iterate the adjacency + list of H(v) and, for each neighbor w, we mark G(w). Then, we + iterate the adjacency list of G(v) and unmark each neighbor. Then, + we iterate the adjacency list of H(v) again to ensure that every + neighbor w was unmarked. If there exists a marked neighbor, then + H(v) contains an incident edge that is not incident to G(v). + + Returns TRUE if theSubgraph contains only edges from theGraph, + FALSE otherwise + ********************************************************************/ + +int _TestSubgraph(graphP theSubgraph, graphP theGraph) +{ + int v, e, degreeCount; + int Result = TRUE; + int invokeSortOnGraph = FALSE; + int invokeSortOnSubgraph = FALSE; + + // If the graph is not sorted by DFI, but the alleged subgraph is, + // then "unsort" the alleged subgraph so both have the same vertex order + if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI) && + (theSubgraph->internalFlags & FLAGS_SORTEDBYDFI)) + { + invokeSortOnSubgraph = TRUE; + gp_SortVertices(theSubgraph); + } + + // If the graph is not sorted by DFI, but the alleged subgraph is, + // then "unsort" the alleged subgraph so both have the same vertex order + if (!(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI) && + (theGraph->internalFlags & FLAGS_SORTEDBYDFI)) + { + invokeSortOnGraph = TRUE; + gp_SortVertices(theGraph); + } + + /* We clear all visitation flags */ + + _ClearVertexVisitedFlags(theGraph, FALSE); + + /* For each vertex... */ + for (v = gp_GetFirstVertex(theSubgraph), degreeCount = 0; gp_VertexInRange(theSubgraph, v); v++) + { + /* For each neighbor w in the adjacency list of vertex v in the + subgraph, set the visited flag in w in the graph */ + + e = gp_GetFirstArc(theSubgraph, v); + while (gp_IsArc(e)) + { + if (gp_IsNotVertex(gp_GetNeighbor(theSubgraph, e))) + { + Result = FALSE; + break; + } + degreeCount++; + gp_SetVertexVisited(theGraph, gp_GetNeighbor(theSubgraph, e)); + e = gp_GetNextArc(theSubgraph, e); + } + + if (Result != TRUE) + break; + + /* For each neighbor w in the adjacency list of vertex v in the graph, + clear the visited flag in w in the graph */ + + e = gp_GetFirstArc(theGraph, v); + while (gp_IsArc(e)) + { + if (gp_IsNotVertex(gp_GetNeighbor(theGraph, e))) + { + Result = FALSE; + break; + } + gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); + e = gp_GetNextArc(theGraph, e); + } + + if (Result != TRUE) + break; + + /* For each neighbor w in the adjacency list of vertex v in the subgraph, + ensure that the visited flag in w was cleared (otherwise, the "subgraph" + would incorrectly contain an adjacency not contained in the ("super") graph) */ + + e = gp_GetFirstArc(theSubgraph, v); + while (gp_IsArc(e)) + { + if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theSubgraph, e))) + { + Result = FALSE; + break; + } + e = gp_GetNextArc(theSubgraph, e); + } + + if (Result != TRUE) + break; + } + + // Restore the DFI sort order of either graph if it had to be reordered at the start + if (invokeSortOnSubgraph) + gp_SortVertices(theSubgraph); + if (invokeSortOnGraph) + gp_SortVertices(theGraph); + + // Assuming theSubgraph is a subgraph, we also do an extra integrity check to ensure + // proper edge array utilization + if (Result == TRUE) + { + // If the edge count is wrong, we fail the subgraph test in a way that invokes + // the name NOTOK so that in debug mode there is more trace on the failure. + if (degreeCount != 2 * theSubgraph->M) + Result = NOTOK == FALSE ? NOTOK : FALSE; + } + + return Result; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.1 b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.1 new file mode 100644 index 000000000..8bf51cc95 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.1 @@ -0,0 +1,130 @@ +.TH planarity 1 + +.SH NAME +planarity - The Edge Addition Planarity Suite + +.SH SYNOPSIS + +.SS Getting help +.B planarity -h|-help [-menu] + +.B planarity -i|-info + +.SS Menu-driven mode +.B planarity + +.SS Batch modes + +.B planarity -s [-q] \fICOMMAND\fR \fIINPUT\fR \fIOUTPUT\fR [\fICOMPLEMENT\fR] + +.B planarity -r [-q] \fICOMMAND\fR \fIK\fR \fIN\fR [\fIOUTPUT\fR] + +.B planarity -rm [-q] \fIN\fR \fIOUTPUT\fR [\fICOMPLEMENT\fR] + +.B planarity -rn [-q] \fIN\fR \fIOUTPUT\fR [\fICOMPLEMENT\fR] + +.B planarity -x [-q] \fB-(gam)\fR \fIINPUT\fR \fIOUTPUT\fR + +.B planarity -t [-q] \fICOMMAND\fR \fIINPUT\fR \fIOUTPUT\fR + +.SH DESCRIPTION +Invokes the Edge Addition Planarity Suite command-line tool, either in +interactive mode or in batch mode. + +Without a parameter, the tool presents a menu-driven interactive +interface. + +When a parameter is given, it runs in batch mode and returns 0 or 1 +for a successful result and -1 on error. + +.SH OPTIONS + +.TP +.B -h, -help [-menu] +Display some help, with \fB-menu\fR triggering even more detailed output. + +.TP +.B -i, -info +Display version, copyright, license, and reference articles information. + +.TP +.B -q +Quiet mode optional modifier, see below which options accept it. + +.TP +.B -s [-q] \fICOMMAND\fR \fIINPUT\fR \fIOUTPUT\fR [\fICOMPLEMENT\fR] +Run the \fICOMMAND\fR (see below) on a specific graph given in the +\fIINPUT\fR file, with output in the primary \fIOUTPUT\fR file and +complementary information, if any, in the secondary \fICOMPLEMENT\fR file +(e.g. an ASCII art rendition for the planar graph drawing command). The +return value is 0 if the specific graph is embeddable (e.g. it is +planar or doesn't contain a homeomorphic subgraph) and 1 if it isn't +embeddable (e.g. non planar or does contain a homeomorphic subgraph). + +.TP +.B -r [-q] \fICOMMAND\fR \fIK\fR \fIN\fR [\fIOUTPUT\fR] +Run the \fICOMMAND\fR (see below) on \fIK\fR random graphs with +\fIN\fR vertices. Optionally, write all generated graphs to .g6 \fIOUTPUT\fR. + +.TP +.B -rm [-q] \fIN\fR \fIOUTPUT\fR [\fICOMPLEMENT\fR] +Generate a random maximal planar graph with \fIN\fR vertices, then output +its planar embedding in the primary \fIOUTPUT\fR file and optionally the +generated graph in the \fICOMPLEMENT\fR file. + +.TP +.B -rn [-q] \fIN\fR \fIOUTPUT\fR [\fICOMPLEMENT\fR] +Generate a random nonplanar graph (maximal planar plus one edge) with +\fIN\fR vertices, then output a Kuratowski subgraph of the generated graph +in the primary \fIOUTPUT\fR file and optionally the gnerated graph in +the \fICOMPLEMENT\fR file. + +.TP +.B [-q] \fB-(gam)\fR \fIINPUT\fR \fIOUTPUT\fR +Transform single graph in \fIINPUT\fR file (any supported format) to .g6 (\fBg\fR), +adjacency list (\fBa\fR), or adjacency matrix (\fBm\fR) format and output +to \fIOUTPUT\fR file. + +.TP +.B [-q] \fICOMMAND\fR \fIINPUT\fR \fIOUTPUT\fR +Run the \fICOMMAND\fR (see below) on graphs in .g6 encoded \fIINPUT\fR +file, then output summary statistics to \fIOUTPUT\fR file. + +.SH COMMANDS +Determine which algorithm implementation to run: +.TP +.B -p +Planar embedding and Kuratowski subgraph isolation +.TP +.B -d +Planar graph drawing by visibility representation (and optional ASCII art rendition) +.TP +.B -o +Outerplanar embedding and obstruction isolation +.TP +.B -2 +Search for subgraph homeomorphic to K_{2,3} +.TP +.B -3 +Search for subgraph homeomorphic to K_{3,3} +.TP +.B -4 +Search for subgraph homeomorphic to K_4 + +.SH EXAMPLES +.TP +.B planarity -s -q -p infile.txt embedding.out [obstruction.out] +Process infile.txt in quiet mode (-q), putting planar embedding in +embedding.out or (optionally) a Kuratowski subgraph in obstruction.out. +Process returns 0=planar, 1=nonplanar, -1=error +.TP +.B planarity -s -q -d infile.txt embedding.out [drawing.out] +If graph in infile.txt is planar, then put embedding in embedding.out +and (optionally) an ASCII art drawing in drawing.out. +Process returns 0=planar, 1=nonplanar, -1=error + +.SH SEE ALSO + +The full inline help: \fBplanarity -h -menu\fR + +The project homepage: \fBhttps://github.com/graph-algorithms/edge-addition-planarity-suite\fR diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.c new file mode 100644 index 000000000..26d0aff89 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.c @@ -0,0 +1,30 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +/**************************************************************************** + MAIN + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int retVal = 0; + + if (argc <= 1) + retVal = menu(); + + else if (argv[1][0] == '-') + retVal = commandLine(argc, argv); + + else + retVal = legacyCommandLine(argc, argv); + + // Close the log file if logging + gp_Log(NULL); + + return retVal; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.h new file mode 100644 index 000000000..6175e6145 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarity.h @@ -0,0 +1,76 @@ +#ifndef PLANARITY_H +#define PLANARITY_H + +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../graphLib/graphLib.h" + + char *GetProjectTitle(void); + char *GetAlgorithmFlags(void); + char *GetAlgorithmSpecifiers(void); + char *GetAlgorithmChoices(void); + char *GetSupportedOutputChoices(void); + char *GetSupportedOutputFormats(void); + + int helpMessage(char *param); + + /* Functions that call the Graph Library */ + int SpecificGraph( + char command, + char *infileName, char *outfileName, char *outfile2Name, + char *inputStr, char **pOutputStr, char **pOutput2Str); + int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name); + int RandomGraphs(char command, int NumGraphs, int SizeOfGraphs, char *outfileName); + int TransformGraph(char *commandString, char *infileName, char *inputStr, int *outputBase, char *outfileName, char **outputStr); + int TestAllGraphs(char *commandString, char *infileName, char *outfileName, char **outputStr); + + /* Command line, Menu, and Configuration */ + int menu(void); + int commandLine(int argc, char *argv[]); + int legacyCommandLine(int argc, char *argv[]); + + extern char Mode, + OrigOut, + OrigOutFormat, + EmbeddableOut, + ObstructedOut, + AdjListsForEmbeddingsOut; + + void Reconfigure(void); + + /* Low-level Utilities */ + void FlushConsole(FILE *f); + void Prompt(char *message); + + void SaveAsciiGraph(graphP theGraph, char *filename); + + char *ReadTextFileIntoString(char *infileName); + int TextFileMatchesString(char *theFilename, char *theString); + int TextFilesEqual(char *file1Name, char *file2Name); + int BinaryFilesEqual(char *file1Name, char *file2Name); + + int GetEmbedFlags(char command); + char *GetAlgorithmName(char command); + char *GetTransformationName(char command); + char *GetBaseName(int baseFlag); + void AttachAlgorithm(graphP theGraph, char command); + + char *ConstructInputFilename(char *infileName); + char *ConstructPrimaryOutputFilename(char *infileName, char *outfileName, char command); + int ConstructTransformationExpectedResultFilename(char *infileName, char **outfileName, char command, int actualOrExpectedFlag); + void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityCommandLine.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityCommandLine.c new file mode 100644 index 000000000..fcd5b05f6 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityCommandLine.c @@ -0,0 +1,781 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER) +// MSVC under Windows doesn't have unistd.h, but does define functions like getcwd and chdir +#include +#define getcwd _getcwd +#define chdir _chdir +#else +#include +#endif + +int runQuickRegressionTests(int argc, char *argv[]); +int callRandomGraphs(int argc, char *argv[]); +int callSpecificGraph(int argc, char *argv[]); +int callRandomMaxPlanarGraph(int argc, char *argv[]); +int callRandomNonplanarGraph(int argc, char *argv[]); +int callTestAllGraphs(int argc, char *argv[]); +int callTransformGraph(int argc, char *argv[]); + +/**************************************************************************** + Command Line Processor + ****************************************************************************/ + +int commandLine(int argc, char *argv[]) +{ + int Result = OK; + + if (argc >= 3 && strcmp(argv[2], "-q") == 0) + setQuietModeSetting(TRUE); + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0) + { + Result = helpMessage(argc >= 3 ? argv[2] : NULL); + } + + else if (strcmp(argv[1], "-i") == 0 || strcmp(argv[1], "-info") == 0) + { + Result = helpMessage(argv[1]); + } + + else if (strcmp(argv[1], "-test") == 0) + Result = runQuickRegressionTests(argc, argv); + + else if (strcmp(argv[1], "-r") == 0) + Result = callRandomGraphs(argc, argv); + + else if (strcmp(argv[1], "-s") == 0) + Result = callSpecificGraph(argc, argv); + + else if (strcmp(argv[1], "-rm") == 0) + Result = callRandomMaxPlanarGraph(argc, argv); + + else if (strcmp(argv[1], "-rn") == 0) + Result = callRandomNonplanarGraph(argc, argv); + + else if (strncmp(argv[1], "-x", 2) == 0) + Result = callTransformGraph(argc, argv); + + else if (strncmp(argv[1], "-t", 2) == 0) + Result = callTestAllGraphs(argc, argv); + + else + { + ErrorMessage("Unsupported command line. Here is the help for this program.\n"); + helpMessage(NULL); + Result = NOTOK; + } + +#ifdef DEBUG + // When one builds and runs the executable in an external console from an IDE + // such as VSCode, the external console window will close immediately upon + // exit 0 being returned. This means that one may miss Messages and + // ErrorMessages that are crucial to the debugging process. Hence, if we compile + // with the DDEBUG flag, this means that in appconst.h we #define DEBUG. That way, + // this prompt will appear only for debug builds, and will ensure the console + // window stays open until the user proceeds. + printf("\n\tPress return key to exit...\n"); + fflush(stdout); + fflush(stdin); + getc(stdin); +#endif + return Result == OK ? 0 : (Result == NONEMBEDDABLE ? 1 : -1); +} + +/**************************************************************************** + Legacy Command Line Processor from version 1.x + ****************************************************************************/ + +int legacyCommandLine(int argc, char *argv[]) +{ + graphP theGraph = gp_New(); + int Result; + + Result = gp_Read(theGraph, argv[1]); + if (Result != OK) + { + if (Result != NONEMBEDDABLE) + { + char *messageFormat = "Failed to read graph \"%.*s\""; + char messageContents[MAXLINE + 1]; + int charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForFilename, argv[1]); + ErrorMessage(messageContents); + return -2; + } + } + + Result = gp_Embed(theGraph, EMBEDFLAGS_PLANAR); + + if (Result == OK) + { + gp_SortVertices(theGraph); + gp_Write(theGraph, argv[2], WRITE_ADJLIST); + } + + else if (Result == NONEMBEDDABLE) + { + if (argc >= 5 && strcmp(argv[3], "-n") == 0) + { + gp_SortVertices(theGraph); + gp_Write(theGraph, argv[4], WRITE_ADJLIST); + } + } + else + Result = NOTOK; + + gp_Free(&theGraph); + + // In the legacy 1.x versions, OK/NONEMBEDDABLE was 0 and NOTOK was -2 + return Result == OK || Result == NONEMBEDDABLE ? 0 : -2; +} + +/**************************************************************************** + Quick regression test + ****************************************************************************/ + +int runSpecificGraphTests(char *); +int runSpecificGraphTest(char *command, char *infileName, int inputInMemFlag); +int runGraphTransformationTest(char *command, char *infileName, int inputInMemFlag); + +int runQuickRegressionTests(int argc, char *argv[]) +{ + char *samplesDir = "samples"; + int samplesDirArgLocation = 2; + + // Skip optional -q quiet mode command-line paramater, if present + if (argc > samplesDirArgLocation && strcmp(argv[samplesDirArgLocation], "-q") == 0) + samplesDirArgLocation++; + + // Accept overriding sample directory command-line parameter, if present + if (argc > samplesDirArgLocation) + samplesDir = argv[samplesDirArgLocation]; + + if (runSpecificGraphTests(samplesDir) < 0) + return NOTOK; + + return OK; +} + +int runSpecificGraphTests(char *samplesDir) +{ + char origDir[2049]; + int retVal = 0; + + if (!getcwd(origDir, 2048)) + return -1; + + // Preserve original behavior before the samplesDir command-line parameter was available + if (strcmp(samplesDir, "samples") == 0) + { + if (chdir(samplesDir) != 0) + { + if (chdir("..") != 0 || chdir(samplesDir) != 0) + { + // Give success result, but Warn if no samples (except no warning if in quiet mode) + Message("WARNING: Unable to change to samples directory to run tests on samples.\n"); + return 0; + } + } + } + else + { + // New behavior if samplesDir command-line parameter was specified + if (chdir(samplesDir) != 0) + { + Message("WARNING: Unable to change to samples directory to run tests on samples.\n"); + return 0; + } + } + +#ifdef USE_FASTER_1BASEDARRAYS + Message("\n\tStarting 1-based Array Index Tests\n\n"); + + if (runSpecificGraphTest("-p", "maxPlanar5.txt", TRUE) < 0) + { + retVal = -1; + Message("Planarity test on maxPlanar5.txt failed.\n"); + } + + if (runSpecificGraphTest("-d", "maxPlanar5.txt", FALSE) < 0) + { + retVal = -1; + Message("Graph drawing test maxPlanar5.txt failed.\n"); + } + + if (runSpecificGraphTest("-d", "drawExample.txt", TRUE) < 0) + { + retVal = -1; + Message("Graph drawing on drawExample.txt failed.\n"); + } + + if (runSpecificGraphTest("-p", "Petersen.txt", FALSE) < 0) + { + retVal = -1; + Message("Planarity test on Petersen.txt failed.\n"); + } + + if (runSpecificGraphTest("-o", "Petersen.txt", TRUE) < 0) + { + retVal = -1; + Message("Outerplanarity test on Petersen.txt failed.\n"); + } + + if (runSpecificGraphTest("-2", "Petersen.txt", FALSE) < 0) + { + retVal = -1; + Message("K_{2,3} search on Petersen.txt failed.\n"); + } + + if (runSpecificGraphTest("-3", "Petersen.txt", TRUE) < 0) + { + retVal = -1; + Message("K_{3,3} search on Petersen.txt failed.\n"); + } + + if (runSpecificGraphTest("-4", "Petersen.txt", FALSE) < 0) + { + retVal = -1; + Message("K_4 search on Petersen.txt failed.\n"); + } + + Message("\tFinished 1-based Array Index Tests.\n\n"); +#endif + + if (runSpecificGraphTest("-p", "maxPlanar5.0-based.txt", FALSE) < 0) + { + retVal = -1; + Message("Planarity test on maxPlanar5.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-d", "maxPlanar5.0-based.txt", TRUE) < 0) + { + retVal = -1; + Message("Graph drawing test maxPlanar5.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-d", "drawExample.0-based.txt", FALSE) < 0) + { + retVal = -1; + Message("Graph drawing on drawExample.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-p", "Petersen.0-based.txt", TRUE) < 0) + { + retVal = -1; + Message("Planarity test on Petersen.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-o", "Petersen.0-based.txt", FALSE) < 0) + { + retVal = -1; + Message("Outerplanarity test on Petersen.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-2", "Petersen.0-based.txt", TRUE) < 0) + { + retVal = -1; + Message("K_{2,3} search on Petersen.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-3", "Petersen.0-based.txt", FALSE) < 0) + { + retVal = -1; + Message("K_{3,3} search on Petersen.0-based.txt failed.\n"); + } + + if (runSpecificGraphTest("-4", "Petersen.0-based.txt", TRUE) < 0) + { + retVal = -1; + Message("K_4 search on Petersen.0-based.txt failed.\n"); + } + + /* + GRAPH TRANSFORMATION TESTS + */ + // TRANSFORM TO ADJACENCY LIST + + // runGraphTransformationTest by reading file contents into string + if (runGraphTransformationTest("-a", "nauty_example.g6", TRUE) < 0) + { + retVal = -1; + Message("Transforming nauty_example.g6 file contents as string to adjacency list failed.\n"); + } + + // runGraphTransformationTest by reading from file + if (runGraphTransformationTest("-a", "nauty_example.g6", FALSE) < 0) + { + retVal = -1; + Message("Transforming nauty_example.g6 using file pointer to adjacency list failed.\n"); + } + + // runGraphTransformationTest by reading first graph from file into string + if (runGraphTransformationTest("-a", "N5-all.g6", TRUE) < 0) + { + retVal = -1; + Message("Transforming first graph in N5-all.g6 (read as string) to adjacency list failed.\n"); + } + + // runGraphTransformationTest by reading first graph from file pointer + if (runGraphTransformationTest("-a", "N5-all.g6", FALSE) < 0) + { + retVal = -1; + Message("Transforming first graph in N5-all.g6 (read from file pointer) to adjacency list failed.\n"); + } + + // runGraphTransformationTest by reading file contents corresponding to dense graph into string + if (runGraphTransformationTest("-a", "K10.g6", TRUE) < 0) + { + retVal = -1; + Message("Transforming K10.g6 file contents as string to adjacency list failed.\n"); + } + + // runGraphTransformationTest by reading dense graph from file + if (runGraphTransformationTest("-a", "K10.g6", FALSE) < 0) + { + retVal = -1; + Message("Transforming K10.g6 using file pointer to adjacency list failed.\n"); + } + + // TRANSFORM TO ADJACENCY MATRIX + + // runGraphTransformationTest by reading file contents into string + if (runGraphTransformationTest("-m", "nauty_example.g6", TRUE) < 0) + { + retVal = -1; + Message("Transforming nauty_example.g6 file contents as string to adjacency matrix failed.\n"); + } + + // runGraphTransformationTest by reading from file + if (runGraphTransformationTest("-m", "nauty_example.g6", FALSE) < 0) + { + retVal = -1; + Message("Transforming nauty_example.g6 using file pointer to adjacency matrix failed.\n"); + } + + // runGraphTransformationTest by reading first graph from file into string + if (runGraphTransformationTest("-m", "N5-all.g6", TRUE) < 0) + { + retVal = -1; + Message("Transforming first graph in N5-all.g6 (read as string) to adjacency matrix failed.\n"); + } + + // runGraphTransformationTest by reading first graph from file pointer + if (runGraphTransformationTest("-m", "N5-all.g6", FALSE) < 0) + { + retVal = -1; + Message("Transforming first graph in N5-all.g6 (read from file pointer) to adjacency matrix failed.\n"); + } + + // runGraphTransformationTest by reading file contents corresponding to dense graph into string + if (runGraphTransformationTest("-m", "K10.g6", TRUE) < 0) + { + retVal = -1; + Message("Transforming K10.g6 file contents as string to adjacency matrix failed.\n"); + } + + // runGraphTransformationTest by reading dense graph from file + if (runGraphTransformationTest("-m", "K10.g6", FALSE) < 0) + { + retVal = -1; + Message("Transforming K10.g6 using file pointer to adjacency matrix failed.\n"); + } + + // TRANSFORM TO .G6 + + // runGraphTransformationTest by reading from file + if (runGraphTransformationTest("-g", "nauty_example.g6.0-based.AdjList.out.txt", TRUE) < 0) + { + retVal = -1; + Message("Transforming nauty_example.g6.0-based.AdjList.out.txt using file pointer to .g6 failed.\n"); + } + + // runGraphTransformationTest by reading from file + if (runGraphTransformationTest("-g", "K10.g6.0-based.AdjList.out.txt", TRUE) < 0) + { + retVal = -1; + Message("Transforming K10.g6.0-based.AdjList.out.txt using file pointer to .g6 failed.\n"); + } + + if (retVal == 0) + Message("Tests of all specific graphs succeeded.\n"); + else + Message("One or more specific graph tests FAILED.\n"); + + chdir(origDir); + FlushConsole(stdout); + return retVal; +} + +int runSpecificGraphTest(char *command, char *infileName, int inputInMemFlag) +{ + int Result = OK; + char algorithmCode = command[1]; + + // The algorithm, indicated by algorithmCode, operating on 'infilename' is expected to produce + // an output that is stored in the file named 'expectedResultFileName' (return string not owned) + char *expectedPrimaryResultFileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]); + + char *inputString = NULL; + char *actualOutput = NULL; + char *actualOutput2 = NULL; + + // SpecificGraph() can invoke gp_Read() if the graph is to be read from a file, or it can invoke + // gp_ReadFromString() if the inputInMemFlag is set. + if (inputInMemFlag) + { + inputString = ReadTextFileIntoString(infileName); + if (inputString == NULL) + { + ErrorMessage("Failed to read input file into string.\n"); + Result = NOTOK; + } + } + + if (Result == OK) + { + // Perform the indicated algorithm on the graph in the input file or string. gp_ReadFromString() + // will handle freeing inputString. + Result = SpecificGraph(algorithmCode, + infileName, NULL, NULL, + inputString, &actualOutput, &actualOutput2); + } + + // Change from internal OK/NONEMBEDDABLE/NOTOK result to a command-line style 0/-1 result + if (Result == OK || Result == NONEMBEDDABLE) + Result = 0; + else + { + ErrorMessage("Test failed (graph processor returned failure result).\n"); + Result = -1; + } + + // Test that the primary actual output matches the primary expected output + if (Result == 0) + { + if (TextFileMatchesString(expectedPrimaryResultFileName, actualOutput) == TRUE) + Message("Test succeeded (result equal to exemplar).\n"); + else + { + ErrorMessage("Test failed (result not equal to exemplar).\n"); + Result = -1; + } + } + + // Test that the secondary actual output matches the secondary expected output + if (algorithmCode == 'd' && Result == 0) + { + char *expectedSecondaryResultFileName = (char *)malloc(strlen(expectedPrimaryResultFileName) + strlen(".render.txt") + 1); + + if (expectedSecondaryResultFileName != NULL) + { + sprintf(expectedSecondaryResultFileName, "%s%s", expectedPrimaryResultFileName, ".render.txt"); + + if (TextFileMatchesString(expectedSecondaryResultFileName, actualOutput2) == TRUE) + Message("Test succeeded (secondary result equal to exemplar).\n"); + else + { + ErrorMessage("Test failed (secondary result not equal to exemplar).\n"); + Result = -1; + } + + free(expectedSecondaryResultFileName); + } + else + { + Result = -1; + } + } + + // Cleanup and then return the command-line style result code + Message("\n"); + + if (actualOutput != NULL) + free(actualOutput); + if (actualOutput2 != NULL) + free(actualOutput2); + + return Result; +} + +int runGraphTransformationTest(char *command, char *infileName, int inputInMemFlag) +{ + int Result = OK; + + char transformationCode = '\0'; + // runGraphTransformationTest will not test performing an algorithm on a given + // input graph; it will only support "-(gam)" + if (command == NULL || strlen(command) < 2) + { + ErrorMessage("runGraphTransformationTest only supports -(gam).\n"); + return NOTOK; + } + else if (strlen(command) == 2) + transformationCode = command[1]; + + // SpecificGraph() can invoke gp_Read() if the graph is to be read from a file, or it can invoke + // gp_ReadFromString() if the inputInMemFlag is set. + char *inputString = NULL; + if (inputInMemFlag) + { + inputString = ReadTextFileIntoString(infileName); + if (inputString == NULL) + { + ErrorMessage("Failed to read input file into string.\n"); + Result = NOTOK; + } + } + + if (Result == OK) + { + // We need to capture whether output is 0- or 1-based to construct the name of the file to compare actualOutput with + int zeroBasedOutputFlag = 0; + char *actualOutput = NULL; + // We want to handle the test being run when we read from an input file or read from a string, + // so pass both infileName and inputString. Ownership of inputString is relinquished to TransformGraph(), + // and gp_ReadFromString() will handle freeing it. + // We want to output to string, so we pass in the address of the actualOutput string. + Result = TransformGraph(command, infileName, inputString, &zeroBasedOutputFlag, NULL, &actualOutput); + + if (Result != OK || actualOutput == NULL) + { + ErrorMessage("Failed to perform transformation.\n"); + } + else + { + // Final arg is baseFlag, which is dependent on whether the FLAGS_ZEROBASEDIO is set in a graphP's internalFlags + char *expectedOutfileName = NULL; + Result = ConstructTransformationExpectedResultFilename(infileName, &expectedOutfileName, transformationCode, zeroBasedOutputFlag ? 0 : 1); + + if (Result != OK || expectedOutfileName == NULL) + { + ErrorMessage("Unable to construct output filename for expected transformation output.\n"); + return -1; + } + + Result = TextFileMatchesString(expectedOutfileName, actualOutput); + + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + int charsAvailForFilename = 0; + if (Result == TRUE) + { + messageFormat = "For the transformation %s on file \"%.*s\", actual output file matched expected output file.\n"; + charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, command, charsAvailForFilename, infileName); + Message(messageContents); + Result = OK; + } + else + { + messageFormat = "For the transformation %s on file \"%.*s\", actual output file did not match expected output file.\n"; + charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, command, charsAvailForFilename, infileName); + ErrorMessage(messageContents); + Result = NOTOK; + } + + if (expectedOutfileName != NULL) + { + free(expectedOutfileName); + expectedOutfileName = NULL; + } + } + } + + Message("\n"); + + return (Result == OK) ? 0 : -1; +} + +/**************************************************************************** + callRandomGraphs() + ****************************************************************************/ + +// 'planarity -r [-q] C K N [O]': Random graphs +int callRandomGraphs(int argc, char *argv[]) +{ + char Choice = 0; + int offset = 0, NumGraphs, SizeOfGraphs; + char *outfileName = NULL; + + if (argc < 5 || argc > 7) + return -1; + + if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q') + { + Choice = argv[3][1]; + if (argc < 6) + return -1; + offset = 1; + } + + NumGraphs = atoi(argv[3 + offset]); + SizeOfGraphs = atoi(argv[4 + offset]); + + if (argc == (6 + offset)) + { + outfileName = argv[5 + offset]; + } + + return RandomGraphs(Choice, NumGraphs, SizeOfGraphs, outfileName); +} + +/**************************************************************************** + callSpecificGraph() + ****************************************************************************/ + +// 'planarity -s [-q] C I O [O2]': Specific graph +int callSpecificGraph(int argc, char *argv[]) +{ + char Choice = 0, *infileName = NULL, *outfileName = NULL, *outfile2Name = NULL; + int offset = 0; + + if (argc < 5) + return -1; + + if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q') + { + Choice = argv[3][1]; + if (argc < 6) + return -1; + offset = 1; + } + + infileName = argv[3 + offset]; + outfileName = argv[4 + offset]; + if (argc == 6 + offset) + outfile2Name = argv[5 + offset]; + + return SpecificGraph(Choice, infileName, outfileName, outfile2Name, NULL, NULL, NULL); +} + +/**************************************************************************** + callRandomMaxPlanarGraph() + ****************************************************************************/ + +// 'planarity -rm [-q] N O [O2]': Maximal planar random graph +int callRandomMaxPlanarGraph(int argc, char *argv[]) +{ + int offset = 0, numVertices; + char *outfileName = NULL, *outfile2Name = NULL; + + if (argc < 4) + return -1; + + if (argv[2][0] == '-' && argv[2][1] == 'q') + { + if (argc < 5) + return -1; + offset = 1; + } + + numVertices = atoi(argv[2 + offset]); + outfileName = argv[3 + offset]; + if (argc == 5 + offset) + outfile2Name = argv[4 + offset]; + + return RandomGraph('p', 0, numVertices, outfileName, outfile2Name); +} + +/**************************************************************************** + callRandomNonplanarGraph() + ****************************************************************************/ + +// 'planarity -rn [-q] N O [O2]': Non-planar random graph (maximal planar plus edge) +int callRandomNonplanarGraph(int argc, char *argv[]) +{ + int offset = 0, numVertices; + char *outfileName = NULL, *outfile2Name = NULL; + + if (argc < 4) + return -1; + + if (argv[2][0] == '-' && argv[2][1] == 'q') + { + if (argc < 5) + return -1; + offset = 1; + } + + numVertices = atoi(argv[2 + offset]); + outfileName = argv[3 + offset]; + if (argc == 5 + offset) + outfile2Name = argv[4 + offset]; + + return RandomGraph('p', 1, numVertices, outfileName, outfile2Name); +} + +/**************************************************************************** + callTransformGraph() + ****************************************************************************/ + +// 'planarity -x [-q] -(gam) I O': Input file I is transformed from its given +// format to the format given by the g (g6), a (adjacency list) or m (matrix), +// and written to output file O. +int callTransformGraph(int argc, char *argv[]) +{ + int offset = 0; + char *commandString = NULL; + char *infileName = NULL, *outfileName = NULL; + + if (argc < 5) + return -1; + + if (argv[2][0] == '-' && argv[2][1] == 'q') + { + if (argc < 6) + return -1; + offset = 1; + } + + commandString = argv[2 + offset]; + + infileName = argv[3 + offset]; + outfileName = argv[4 + offset]; + + // We don't want to read from string, so inputStr is NULL + // We don't want to write to string, so outputStr is NULL + // We don't need to capture whether output is 0- or 1-based, so zeroBasedOutputFlag arg is NULL + return TransformGraph(commandString, infileName, NULL, NULL, outfileName, NULL); +} + +/**************************************************************************** + callTestAllGraphs() + ****************************************************************************/ + +// 'planarity -t [-q] C I O': If the command line argument after -t [-q] is a +// recognized algorithm command C, then the input file I must be in ".g6" format +// (report an error otherwise), and the algorithm(s) indicated by C are executed +// on the graph(s) in the input file, with the results of the execution stored +// in output file O. +int callTestAllGraphs(int argc, char *argv[]) +{ + int offset = 0; + char *commandString = NULL; + char *infileName = NULL, *outfileName = NULL; + + if (argc < 5) + return -1; + + if (argv[2][0] == '-' && argv[2][1] == 'q') + { + if (argc < 6) + return -1; + offset = 1; + } + + commandString = argv[2 + offset]; + + infileName = argv[3 + offset]; + outfileName = argv[4 + offset]; + + // NOTE: We don't want to write to string, so outputStr is NULL + return TestAllGraphs(commandString, infileName, outfileName, NULL); +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityHelp.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityHelp.c new file mode 100644 index 000000000..424fddd0c --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityHelp.c @@ -0,0 +1,123 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +char *GetProjectTitle(void) +{ + static char projectTitle[MAXLINE + 1]; + sprintf(projectTitle, + "\n===================================================" + "\nThe Edge Addition Planarity Suite version %s" + "\nbased on libPlanarity graph library version %s" + "\nCopyright (c) 1997-2025 by John M. Boyer" + "\nAll rights reserved." + "\nSee the LICENSE.TXT file for licensing information." + "\nContact info: jboyer at acm.org" + "\n===================================================\n", + gp_GetProjectVersionFull(), + gp_GetLibPlanarityVersionFull()); + + return projectTitle; +} + +/**************************************************************************** + helpMessage() + ****************************************************************************/ + +int helpMessage(char *param) +{ + Message(GetProjectTitle()); + + if (param == NULL) + { + Message( + "'planarity': if no command-line, then menu-driven\n" + "'planarity (-h|-help)': this message\n" + "'planarity (-h|-help) -menu': more help with menu-based command line\n" + "'planarity (-i|-info): copyright and license information\n" + "'planarity -test [-q] [samples dir]': runs tests (optional quiet mode)\n" + "\n"); + + Message( + "Common usages\n" + "-------------\n" + "planarity -s -q -p infile.txt embedding.out [obstruction.out]\n" + "Process infile.txt in quiet mode (-q), putting planar embedding in \n" + "embedding.out or (optionally) a Kuratowski subgraph in Obstruction.out\n" + "Process returns 0=planar, 1=nonplanar, -1=error\n" + "\n" + "planarity -s -q -d infile.txt embedding.out [drawing.out]\n" + "If graph in infile.txt is planar, then put embedding in embedding.out \n" + "and (optionally) an ASCII art drawing in drawing.out\n" + "Process returns 0=planar, 1=nonplanar, -1=error\n"); + } + + else if (strcmp(param, "-i") == 0 || strcmp(param, "-info") == 0) + { + Message( + "Includes a reference implementation of the following:\n" + "\n" + "* John M. Boyer. \"Subgraph Homeomorphism via the Edge Addition Planarity \n" + " Algorithm\". Journal of Graph Algorithms and Applications, Vol. 16, \n" + " no. 2, pp. 381-410, 2012. http://dx.doi.org/10.7155/jgaa.00268\n" + "\n" + "* John M. Boyer. \"A New Method for Efficiently Generating Planar Graph\n" + " Visibility Representations\". In P. Eades and P. Healy, editors,\n" + " Proceedings of the 13th International Conference on Graph Drawing 2005,\n" + " Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.\n" + " http://dx.doi.org/10.1007/11618058_47\n" + "\n" + "* John M. Boyer and Wendy J. Myrvold. \"On the Cutting Edge: Simplified O(n)\n" + " Planarity by Edge Addition\". Journal of Graph Algorithms and Applications,\n" + " Vol. 8, No. 3, pp. 241-273, 2004. http://dx.doi.org/10.7155/jgaa.00091\n" + "\n" + "* John M. Boyer. \"Simplified O(n) Algorithms for Planar Graph Embedding,\n" + " Kuratowski Subgraph Isolation, and Related Problems\". Ph.D. Dissertation,\n" + " University of Victoria, 2001. https://dspace.library.uvic.ca/handle/1828/9918\n" + "\n"); + } + + else if (strcmp(param, "-menu") == 0) + { + Message( + "'planarity -r [-q] C K N [O]': Random graphs\n" + "'planarity -s [-q] C I O [O2]': Specific graph\n" + "'planarity -rm [-q] N O [O2]': Random maximal planar graph\n" + "'planarity -rn [-q] N O [O2]': Random nonplanar graph (maximal planar + edge)\n" + "'planarity -t [-q] C I O': Test algorithm on graph(s) in .g6 file\n" + "'planarity -x [-q] -(gam) I O': Transform graph to .g6 (g), Adjacency List (a), or Adjacency Matrix (m)\n" + "'planarity I O [-n O2]': Legacy command-line (default -s -p)\n" + "\n"); + + Message("-q is for quiet mode (no messages to stdout and stderr)\n\n"); + + Message(GetAlgorithmFlags()); + + Message( + "K = # of graphs to randomly generate\n" + "N = # of vertices in each randomly generated graph\n" + "I = Input file (for work on a specific graph)\n" + "O = Primary output file\n" + " For example, if C=-p then O receives the planar embedding\n" + " If C=-3, then O receives a subgraph containing a K_{3,3}\n" + "O2= Secondary output file\n" + " For -s, if C=-p or -o, then O2 receives the embedding obstruction\n" + " For -s, if C=-d, then O2 receives a drawing of the planar graph\n" + " For -rm and -rn, O2 contains the original randomly generated graph\n" + "\n"); + + Message( + "planarity process results: 0=OK, -1=NOTOK, 1=NONEMBEDDABLE\n" + " 1 result only produced by specific graph mode (-s)\n" + " with command -2,-3,-4: found K_{2,3}, K_{3,3} or K_4\n" + " with command -p,-d: found planarity obstruction\n" + " with command -o: found outerplanarity obstruction\n"); + } + + FlushConsole(stdout); + return OK; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityMenu.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityMenu.c new file mode 100644 index 000000000..d50d9a392 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityMenu.c @@ -0,0 +1,216 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +/**************************************************************************** + MENU-DRIVEN PROGRAM + ****************************************************************************/ +void TransformGraphMenu(void); +void TestAllGraphsMenu(void); + +int menu(void) +{ + char Choice; + + do + { + Message(GetProjectTitle()); + + Message(GetAlgorithmSpecifiers()); + + Message( + "X. Transform single graph in supported file to .g6, adjacency list, or adjacency matrix\n" + "T. Perform an algorithm test on all graphs in .g6 input file\n" + "H. Help message for command line version\n" + "R. Reconfigure options\n" + "Q. Quit\n" + "\n"); + + Prompt("Enter Choice: "); + fflush(stdin); + scanf(" %c", &Choice); + Choice = (char)tolower(Choice); + + if (Choice == 'h') + helpMessage(NULL); + + else if (Choice == 'r') + Reconfigure(); + + else if (Choice == 'x') + TransformGraphMenu(); + + else if (Choice == 't') + TestAllGraphsMenu(); + + else if (Choice != 'q') + { + char *secondOutfile = NULL; + if (Choice == 'p' || Choice == 'd' || Choice == 'o') + secondOutfile = ""; + + if (!strchr(GetAlgorithmChoices(), Choice)) + { + Message("Invalid menu choice, please try again."); + } + else + { + switch (tolower(Mode)) + { + case 's': + SpecificGraph(Choice, NULL, NULL, secondOutfile, NULL, NULL, NULL); + break; + case 'r': + RandomGraphs(Choice, 0, 0, NULL); + break; + case 'm': + RandomGraph(Choice, 0, 0, NULL, NULL); + break; + case 'n': + RandomGraph(Choice, 1, 0, NULL, NULL); + break; + } + } + } + + if (Choice != 'r' && Choice != 'q') + { + Prompt("\nPress a key then hit ENTER to continue..."); + fflush(stdin); + scanf(" %*c"); + fflush(stdin); + Message("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + FlushConsole(stdout); + } + + } while (Choice != 'q'); + + // Certain debuggers don't terminate correctly with pending output content + FlushConsole(stdout); + FlushConsole(stderr); + + return 0; +} + +void TransformGraphMenu(void) +{ + int Result = OK; + + char infileName[MAXLINE + 1]; + infileName[0] = '\0'; + char outfileName[MAXLINE + 1]; + outfileName[0] = '\0'; + char outputFormat = '\0'; + char commandStr[4]; + commandStr[0] = '\0'; + + int numCharsToReprMAXLINE = 0; + if (GetNumCharsToReprInt(MAXLINE, &numCharsToReprMAXLINE) != OK) + { + ErrorMessage("Unable to determine number of characters required to represent MAXLINE.\n"); + return; + } + + char *fileNameFormatFormat = " %%%d[^\r\n]"; + char *fileNameFormat = (char *)malloc((strlen(fileNameFormatFormat) + numCharsToReprMAXLINE + 1) * sizeof(char)); + sprintf(fileNameFormat, fileNameFormatFormat, MAXLINE); + + do + { + Prompt("Enter input filename:\n"); + fflush(stdin); + scanf(fileNameFormat, infileName); + + if (strncmp(infileName, "stdin", strlen("stdin")) == 0) + { + ErrorMessage("\n\tPlease choose an input file path: stdin not supported from menu.\n\n"); + infileName[0] = '\0'; + } + } while (strlen(infileName) == 0); + + do + { + Prompt("Enter output filename, or type \"stdout\" to output to console:\n"); + fflush(stdin); + scanf(fileNameFormat, outfileName); + } while (strlen(outfileName) == 0); + + do + { + Message(GetSupportedOutputChoices()); + Prompt("Enter output format: "); + fflush(stdin); + scanf(" %c", &outputFormat); + outputFormat = (char)tolower(outputFormat); + if (strchr(GetSupportedOutputFormats(), outputFormat)) + sprintf(commandStr, "-%c", outputFormat); + } while (strlen(commandStr) == 0); + + Result = TransformGraph(commandStr, infileName, NULL, NULL, outfileName, NULL); + if (Result != OK) + ErrorMessage("Failed to perform transformation.\n"); +} + +void TestAllGraphsMenu(void) +{ + int Result = OK; + + char infileName[MAXLINE + 1]; + infileName[0] = '\0'; + char outfileName[MAXLINE + 1]; + outfileName[0] = '\0'; + char algorithmSpecifier = '\0'; + char commandStr[3]; + commandStr[0] = '\0'; + + int numCharsToReprMAXLINE = 0; + if (GetNumCharsToReprInt(MAXLINE, &numCharsToReprMAXLINE) != OK) + { + ErrorMessage("Unable to determine number of characters required to represent MAXLINE.\n"); + return; + } + + char *fileNameFormatFormat = " %%%d[^\r\n]"; + char *fileNameFormat = (char *)malloc((strlen(fileNameFormatFormat) + numCharsToReprMAXLINE + 1) * sizeof(char)); + sprintf(fileNameFormat, fileNameFormatFormat, MAXLINE); + + do + { + Prompt("Enter input filename:\n"); + fflush(stdin); + scanf(fileNameFormat, infileName); + + if (strncmp(infileName, "stdin", strlen("stdin")) == 0) + { + ErrorMessage("\n\tPlease choose an input file path: stdin not supported from menu.\n\n"); + infileName[0] = '\0'; + } + } while (strlen(infileName) == 0); + + do + { + Prompt("Enter output filename, or type \"stdout\" to output to console:\n"); + fflush(stdin); + scanf(fileNameFormat, outfileName); + } while (strlen(outfileName) == 0); + + do + { + Message(GetAlgorithmSpecifiers()); + + Prompt("Enter algorithm specifier: "); + fflush(stdin); + scanf(" %c", &algorithmSpecifier); + algorithmSpecifier = (char)tolower(algorithmSpecifier); + if (strchr(GetAlgorithmChoices(), algorithmSpecifier)) + sprintf(commandStr, "-%c", algorithmSpecifier); + } while (strlen(commandStr) == 0); + + Result = TestAllGraphs(commandStr, infileName, outfileName, NULL); + if (Result != OK) + ErrorMessage("Algorithm test on all graphs in .g6 input file failed.\n"); +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityRandomGraphs.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityRandomGraphs.c new file mode 100644 index 000000000..b5098ff3e --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityRandomGraphs.c @@ -0,0 +1,563 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +void GetNumberIfZero(int *pNum, char *prompt, int min, int max); +void ReinitializeGraph(graphP *pGraph, int ReuseGraphs, char command); +graphP MakeGraph(int Size, char command); + +/**************************************************************************** + RandomGraphs() + Top-level method to randomly generate graphs to test the algorithm given by + the command parameter. + The number of graphs to generate, and the number of vertices for each graph, + can be sent as the second and third params. For each that is sent as zero, + this method will prompt the user for a value. + ****************************************************************************/ + +#define NUM_MINORS 9 + +int RandomGraphs(char command, int NumGraphs, int SizeOfGraphs, char *outfileName) +{ + char theFileName[MAXLINE + 1]; + strOrFileP outputContainer = NULL; + int K, countUpdateFreq; + int Result = OK, MainStatistic = 0; + int ObstructionMinorFreqs[NUM_MINORS]; + graphP theGraph = NULL, origGraph = NULL; + platform_time start, end; + int embedFlags = GetEmbedFlags(command); + int ReuseGraphs = TRUE; + int writeResult; + int writeErrorReported_Random = FALSE, writeErrorReported_Embedded = FALSE, + writeErrorReported_AdjList = FALSE, writeErrorReported_Obstructed = FALSE, + writeErrorReported_Error = FALSE; + + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + int charsAvailForStr = 0; + + GetNumberIfZero(&NumGraphs, "Enter number of graphs to generate:", 1, 1000000000); + GetNumberIfZero(&SizeOfGraphs, "Enter size of graphs:", 1, 10000); + + theGraph = MakeGraph(SizeOfGraphs, command); + origGraph = MakeGraph(SizeOfGraphs, command); + if (theGraph == NULL || origGraph == NULL) + { + gp_Free(&theGraph); + return NOTOK; + } + + // Initialize a secondary statistics array + for (K = 0; K < NUM_MINORS; K++) + ObstructionMinorFreqs[K] = 0; + + G6WriteIterator *pG6WriteIterator = NULL; + if (outfileName != NULL || (tolower(OrigOut) == 'y' && tolower(OrigOutFormat) == 'g')) + { + if (allocateG6WriteIterator(&pG6WriteIterator, theGraph) != OK) + { + ErrorMessage("Unable to allocate G6WriteIterator.\n"); + gp_Free(&theGraph); + return NOTOK; + } + } + + messageFormat = "Unable to allocate strOrFile container for outfile \"%.*s\".\n"; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); + if (outfileName != NULL) + { + outputContainer = sf_New(NULL, outfileName, WRITETEXT); + if (outputContainer == NULL) + { + sprintf(messageContents, messageFormat, charsAvailForStr, outfileName); + ErrorMessage(messageContents); + + if (freeG6WriteIterator(&pG6WriteIterator) != OK) + ErrorMessage("Unable to free G6WriteIterator.\n"); + + gp_Free(&theGraph); + + return NOTOK; + } + } + else if (tolower(OrigOut) == 'y' && tolower(OrigOutFormat) == 'g') + { + // If outfileName is NULL, then the only case in which we would want to + // output the generated random graphs to .g6 is if we Reconfigure() and + // choose these options; in that case, need to set a default output filename. + sprintf(theFileName, "random%cn%d.k%d.g6", FILE_DELIMITER, SizeOfGraphs, NumGraphs); + outputContainer = sf_New(NULL, theFileName, WRITETEXT); + if (outputContainer == NULL) + { + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + ErrorMessage(messageContents); + + if (freeG6WriteIterator(&pG6WriteIterator) != OK) + ErrorMessage("Unable to free G6WriteIterator.\n"); + + gp_Free(&theGraph); + + return NOTOK; + } + } + + if (pG6WriteIterator != NULL && outputContainer != NULL) + { + if (beginG6WriteIterationToG6StrOrFile(pG6WriteIterator, outputContainer) != OK) + { + ErrorMessage("Unable to begin writing random graphs to G6WriteIterator.\n"); + + if (freeG6WriteIterator(&pG6WriteIterator) != OK) + ErrorMessage("Unable to free G6WriteIterator.\n"); + + gp_Free(&theGraph); + return NOTOK; + } + } + + // Seed the random number generator with "now". Do it after any prompting + // to tie randomness to human process of answering the prompt. + srand(time(NULL)); + + // Select a counter update frequency that updates more frequently with larger graphs + // and which is relatively prime with 10 so that all digits of the count will change + // even though we aren't showing the count value on every iteration + countUpdateFreq = 3579 / SizeOfGraphs; + countUpdateFreq = countUpdateFreq < 1 ? 1 : countUpdateFreq; + countUpdateFreq = countUpdateFreq % 2 == 0 ? countUpdateFreq + 1 : countUpdateFreq; + countUpdateFreq = countUpdateFreq % 5 == 0 ? countUpdateFreq + 2 : countUpdateFreq; + + // Start the count + fprintf(stdout, "0\r"); + fflush(stdout); + + // Start the timer + platform_GetTime(start); + + messageFormat = "Failed to write graph \"%.*s\"\nMake the directory if not present\n"; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); + // Generate and process the number of graphs requested + for (K = 0; K < NumGraphs; K++) + { + if ((Result = gp_CreateRandomGraph(theGraph)) == OK) + { + if (pG6WriteIterator != NULL) + { + writeResult = writeGraphUsingG6WriteIterator(pG6WriteIterator); + if (writeResult != OK) + { + sprintf(messageContents, "Unable to write graph number %d using G6WriteIterator.\n", K); + ErrorMessage(messageContents); + } + } + if (tolower(OrigOut) == 'y' && tolower(OrigOutFormat) == 'a') + { + sprintf(theFileName, "random%c%d.txt", FILE_DELIMITER, K % 10); + writeResult = gp_Write(theGraph, theFileName, WRITE_ADJLIST); + if (writeResult != OK && !writeErrorReported_Random) + { + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + ErrorMessage(messageContents); + writeErrorReported_Random = TRUE; + } + } + + gp_CopyGraph(origGraph, theGraph); + + if (strchr(GetAlgorithmChoices(), command)) + { + Result = gp_Embed(theGraph, embedFlags); + + if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != Result) + Result = NOTOK; + + if (Result == OK) + { + MainStatistic++; + + if (tolower(EmbeddableOut) == 'y') + { + sprintf(theFileName, "embedded%c%d.txt", FILE_DELIMITER, K % 10); + writeResult = gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); + if (writeResult != OK && !writeErrorReported_Embedded) + { + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + ErrorMessage(messageContents); + writeErrorReported_Embedded = TRUE; + } + } + + if (tolower(AdjListsForEmbeddingsOut) == 'y') + { + sprintf(theFileName, "adjlist%c%d.txt", FILE_DELIMITER, K % 10); + writeResult = gp_Write(theGraph, theFileName, WRITE_ADJLIST); + if (writeResult != OK && !writeErrorReported_AdjList) + { + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + ErrorMessage(messageContents); + writeErrorReported_AdjList = TRUE; + } + } + } + else if (Result == NONEMBEDDABLE) + { + if (embedFlags == EMBEDFLAGS_PLANAR || embedFlags == EMBEDFLAGS_OUTERPLANAR) + { + if (theGraph->IC.minorType & MINORTYPE_A) + ObstructionMinorFreqs[0]++; + else if (theGraph->IC.minorType & MINORTYPE_B) + ObstructionMinorFreqs[1]++; + else if (theGraph->IC.minorType & MINORTYPE_C) + ObstructionMinorFreqs[2]++; + else if (theGraph->IC.minorType & MINORTYPE_D) + ObstructionMinorFreqs[3]++; + else if (theGraph->IC.minorType & MINORTYPE_E) + ObstructionMinorFreqs[4]++; + + if (theGraph->IC.minorType & MINORTYPE_E1) + ObstructionMinorFreqs[5]++; + else if (theGraph->IC.minorType & MINORTYPE_E2) + ObstructionMinorFreqs[6]++; + else if (theGraph->IC.minorType & MINORTYPE_E3) + ObstructionMinorFreqs[7]++; + else if (theGraph->IC.minorType & MINORTYPE_E4) + ObstructionMinorFreqs[8]++; + + if (tolower(ObstructedOut) == 'y') + { + sprintf(theFileName, "obstructed%c%d.txt", FILE_DELIMITER, K % 10); + writeResult = gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); + if (writeResult != OK && !writeErrorReported_Obstructed) + { + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + ErrorMessage(messageContents); + writeErrorReported_Obstructed = TRUE; + } + } + } + } + } + + // If there is an error in processing, then write the file for debugging + if (Result != OK && Result != NONEMBEDDABLE) + { + sprintf(theFileName, "error%c%d.txt", FILE_DELIMITER, K % 10); + writeResult = gp_Write(origGraph, theFileName, WRITE_ADJLIST); + if (writeResult != OK && !writeErrorReported_Error) + { + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + ErrorMessage(messageContents); + writeErrorReported_Error = TRUE; + } + } + } + + // Reinitialize or recreate graphs for next iteration + ReinitializeGraph(&theGraph, ReuseGraphs, command); + ReinitializeGraph(&origGraph, ReuseGraphs, command); + + // Show progress, but not so often that it bogs down progress + if (!getQuietModeSetting() && (K + 1) % countUpdateFreq == 0) + { + fprintf(stdout, "%d\r", K + 1); + fflush(stdout); + } + + // Terminate loop on error + if (Result != OK && Result != NONEMBEDDABLE) + { + ErrorMessage("\nError found\n"); + Result = NOTOK; + break; + } + } + + // Stop the timer + platform_GetTime(end); + + // Finish the count + fprintf(stdout, "%d\n", NumGraphs); + fflush(stdout); + + if (pG6WriteIterator != NULL) + { + if (endG6WriteIteration(pG6WriteIterator) != OK && freeG6WriteIterator(&pG6WriteIterator) != OK) + { + ErrorMessage("Unable to properly terminate .g6 write iteration.\n"); + } + } + // Free the graph structures created before the loop + gp_Free(&theGraph); + gp_Free(&origGraph); + + // Print some demographic results + if (Result == OK || Result == NONEMBEDDABLE) + Message("\nNo Errors Found."); + + sprintf(messageContents, "\nDone (%.3lf seconds).\n", platform_GetDuration(start, end)); + Message(messageContents); + + // Report statistics for planar or outerplanar embedding + if (embedFlags == EMBEDFLAGS_PLANAR || embedFlags == EMBEDFLAGS_OUTERPLANAR) + { + sprintf(messageContents, "Num Embedded=%d.\n", MainStatistic); + Message(messageContents); + + for (K = 0; K < 5; K++) + { + // Outerplanarity does not produces minors C and D + if (embedFlags == EMBEDFLAGS_OUTERPLANAR && (K == 2 || K == 3)) + continue; + + sprintf(messageContents, "Minor %c = %d\n", K + 'A', ObstructionMinorFreqs[K]); + Message(messageContents); + } + + if (!(embedFlags & ~EMBEDFLAGS_PLANAR)) + { + sprintf(messageContents, "\nNote: E1 are added to C, E2 are added to A, and E=E3+E4+K5 homeomorphs.\n"); + Message(messageContents); + + for (K = 5; K < NUM_MINORS; K++) + { + sprintf(messageContents, "Minor E%d = %d\n", K - 4, ObstructionMinorFreqs[K]); + Message(messageContents); + } + } + } + + // Report statistics for graph drawing + else if (embedFlags == EMBEDFLAGS_DRAWPLANAR) + { + sprintf(messageContents, "Num Graphs Embedded and Drawn=%d.\n", MainStatistic); + Message(messageContents); + } + + // Report statistics for subgraph homeomorphism algorithms + else if (embedFlags == EMBEDFLAGS_SEARCHFORK23) + { + sprintf(messageContents, "Of the generated graphs, %d did not contain a K_{2,3} homeomorph as a subgraph.\n", MainStatistic); + Message(messageContents); + } + else if (embedFlags == EMBEDFLAGS_SEARCHFORK33) + { + sprintf(messageContents, "Of the generated graphs, %d did not contain a K_{3,3} homeomorph as a subgraph.\n", MainStatistic); + Message(messageContents); + } + else if (embedFlags == EMBEDFLAGS_SEARCHFORK4) + { + sprintf(messageContents, "Of the generated graphs, %d did not contain a K_4 homeomorph as a subgraph.\n", MainStatistic); + Message(messageContents); + } + + FlushConsole(stdout); + + return Result == OK || Result == NONEMBEDDABLE ? OK : NOTOK; +} + +/**************************************************************************** + GetNumberIfZero() + Internal function that gets a number if the given *pNum is zero. + The prompt is displayed if the number must be obtained from the user. + Whether the given number is used or obtained from the user, the function + ensures it is in the range [min, max] and assigns the midpoint value if + it is not. + ****************************************************************************/ + +void GetNumberIfZero(int *pNum, char *prompt, int min, int max) +{ + if (*pNum == 0) + { + Prompt(prompt); + scanf(" %d", pNum); + } + + if (min < 1) + min = 1; + if (max < min) + max = min; + + if (*pNum < min || *pNum > max) + { + *pNum = (max + min) / 2; + char messageContents[MAXLINE + 1]; + sprintf(messageContents, "Number out of range [%d, %d]; changed to %d\n", min, max, *pNum); + ErrorMessage(messageContents); + } +} + +/**************************************************************************** + MakeGraph() + Internal function that makes a new graph, initializes it, and attaches an + algorithm to it based on the command. + ****************************************************************************/ + +graphP MakeGraph(int Size, char command) +{ + graphP theGraph; + if ((theGraph = gp_New()) == NULL || gp_InitGraph(theGraph, Size) != OK) + { + ErrorMessage("Error creating space for a graph of the given size.\n"); + gp_Free(&theGraph); + return NULL; + } + + // Enable the appropriate feature. Although the same code appears in SpecificGraph, + // it is deliberately not separated to a common utility because SpecificGraph is + // used as a self-contained tutorial. It is not that hard to update both locations + // when new algorithms are added. + + switch (command) + { + case 'd': + gp_AttachDrawPlanar(theGraph); + break; + case '2': + gp_AttachK23Search(theGraph); + break; + case '3': + gp_AttachK33Search(theGraph); + break; + case '4': + gp_AttachK4Search(theGraph); + break; + } + + return theGraph; +} + +/**************************************************************************** + ReinitializeGraph() + Internal function that will either reinitialize the given graph or free it + and make a new one just like it. + ****************************************************************************/ + +void ReinitializeGraph(graphP *pGraph, int ReuseGraphs, char command) +{ + if (ReuseGraphs) + gp_ReinitializeGraph(*pGraph); + else + { + graphP newGraph = MakeGraph((*pGraph)->N, command); + gp_Free(pGraph); + *pGraph = newGraph; + } +} + +/**************************************************************************** + Creates a random maximal planar graph, then adds 'extraEdges' edges to it. + ****************************************************************************/ + +int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name) +{ + int Result; + platform_time start, end; + graphP theGraph = NULL, origGraph; + int embedFlags = GetEmbedFlags(command); + char saveEdgeListFormat; + + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + int charsAvailForStr = 0; + + GetNumberIfZero(&numVertices, "Enter number of vertices:", 1, 1000000); + if ((theGraph = MakeGraph(numVertices, command)) == NULL) + return NOTOK; + + srand(time(NULL)); + + Message("Creating the random graph...\n"); + platform_GetTime(start); + if (gp_CreateRandomGraphEx(theGraph, 3 * numVertices - 6 + extraEdges) != OK) + { + ErrorMessage("gp_CreateRandomGraphEx() failed\n"); + return NOTOK; + } + platform_GetTime(end); + + sprintf(messageContents, "Created random graph with %d edges in %.3lf seconds. ", theGraph->M, platform_GetDuration(start, end)); + Message(messageContents); + FlushConsole(stdout); + + // The user may have requested a copy of the random graph before processing + if (outfile2Name != NULL) + { + gp_Write(theGraph, outfile2Name, WRITE_ADJLIST); + } + + origGraph = gp_DupGraph(theGraph); + + // Do the requested algorithm on the randomly generated graph + Message("Now processing\n"); + FlushConsole(stdout); + + if (strchr(GetAlgorithmChoices(), command)) + { + platform_GetTime(start); + Result = gp_Embed(theGraph, embedFlags); + platform_GetTime(end); + + gp_SortVertices(theGraph); + + if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != Result) + Result = NOTOK; + } + else + Result = NOTOK; + + // Write what the algorithm determined and how long it took + WriteAlgorithmResults(theGraph, Result, command, start, end, NULL); + + // On successful algorithm result, write the output file and see if the + // user wants the edge list formatted file. + if (Result == OK || Result == NONEMBEDDABLE) + { + if (outfileName != NULL) + gp_Write(theGraph, outfileName, WRITE_ADJLIST); + + if (!getQuietModeSetting()) + { + Prompt("Do you want to save the generated graph in edge list format (y/n)? "); + fflush(stdin); + scanf(" %c", &saveEdgeListFormat); + } + else + saveEdgeListFormat = 'n'; + + if (tolower(saveEdgeListFormat) == 'y') + { + char theFileName[MAXLINE + 1]; + + if (extraEdges > 0) + strcpy(theFileName, "nonPlanarEdgeList.txt"); + else + strcpy(theFileName, "maxPlanarEdgeList.txt"); + + messageFormat = "Saving edge list format of original graph to \"%.*s\"\n"; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + Message(messageContents); + SaveAsciiGraph(origGraph, theFileName); + + strcat(theFileName, ".out.txt"); + messageFormat = "Saving edge list format of result to \"%.*s\"\n"; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForStr, theFileName); + Message(messageContents); + SaveAsciiGraph(theGraph, theFileName); + } + } + else + ErrorMessage("Failure occurred"); + + gp_Free(&theGraph); + gp_Free(&origGraph); + + FlushConsole(stdout); + return Result; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planaritySpecificGraph.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planaritySpecificGraph.c new file mode 100644 index 000000000..48a59f114 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planaritySpecificGraph.c @@ -0,0 +1,273 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +/**************************************************************************** + SpecificGraph() + command - a menu letter (e.g. p,d,o,2,3,4) indicating the algorithm to run on the specific graph + infilename - name of file to read, or NULL to cause the program to prompt the user for a filename + outfilename - name of primary output file, or NULL to construct an output filename based on the input + outfile2Name - name of a secondary output file, or NULL to suppress secondary output, or empty string + to construct the secondary output filename based on the output filename. + For p=planarity and o=outerplanarity, empty string means that the planarity or outerplanarity + obstruction will be written to outfilename, rather than only an embedding + For d=drawing a planar graph, empty string means the visibility representation will be + written to outfilename+".render.txt" + inputStr - if non-NULL, overrides infilename and provides the input graph within a string + pOutputStr - if non-NULL, overrides outfilename and provides a pointer pointer where a string containing + the primary output should go. + For p=planarity, o=outerplanarity, and d=drawing, the primary output is the graph embedding + For p=planarity and o=outerplanarity, if the graph is not embeddable, then the primary + output will contain the planarity or outerplanarity obstruction subgraph + For 2,3,4=subgraph homeomorphism, the primary output is the homeomorphic subgraph, if found + pOutput2Str - if non-NULL, overrides outfile2Name and provides a pointer pointer where a string containing + the secondary output should go. + For d=drawing a planar graph, the visibility representation will be written to this + secondary output + ****************************************************************************/ + +int SpecificGraph( + char command, + char *infileName, char *outfileName, char *outfile2Name, + char *inputStr, char **pOutputStr, char **pOutput2Str) +{ + graphP theGraph, origGraph; + platform_time start, end; + int Result = OK; + + // Get the filename of the graph to test + if (inputStr == NULL) + { + if (infileName != NULL) + { + if ((infileName = ConstructInputFilename(infileName)) == NULL) + return NOTOK; + } + else + { + do + { + infileName = ConstructInputFilename(infileName); + if (infileName != NULL && strncmp(infileName, "stdin", strlen("stdin")) == 0) + { + ErrorMessage("\n\tPlease choose an input file path: stdin not supported from menu.\n\n"); + infileName = NULL; + } + } while (infileName == NULL || strlen(infileName) == 0); + } + } + + // Create the graph and, if needed, attach the correct algorithm to it + theGraph = gp_New(); + + // Read the graph into memory + if (inputStr == NULL) + { + Result = gp_Read(theGraph, infileName); + } + else + { + Result = gp_ReadFromString(theGraph, inputStr); + } + + // If there was an unrecoverable error, report it + if (Result != OK) + { + ErrorMessage("Failed to read graph\n"); + } + // Otherwise, call the correct algorithm on it + else + { + // Copy the graph for integrity checking + origGraph = gp_DupGraph(theGraph); + + // Run the algorithm + if (strchr(GetAlgorithmChoices(), command)) + { + switch (command) + { + case 'd': + gp_AttachDrawPlanar(theGraph); + break; + case '2': + gp_AttachK23Search(theGraph); + break; + case '3': + gp_AttachK33Search(theGraph); + break; + case '4': + gp_AttachK4Search(theGraph); + break; + } + + int embedFlags = GetEmbedFlags(command); + platform_GetTime(start); + + // gp_CreateDFSTree(theGraph); + // gp_SortVertices(theGraph); + // gp_Write(theGraph, "debug.before.txt", WRITE_DEBUGINFO); + // gp_SortVertices(theGraph); + + Result = gp_Embed(theGraph, embedFlags); + platform_GetTime(end); + Result = gp_TestEmbedResultIntegrity(theGraph, origGraph, Result); + } + else + { + platform_GetTime(start); + Result = NOTOK; + platform_GetTime(end); + } + + // Write what the algorithm determined and how long it took + WriteAlgorithmResults(theGraph, Result, command, start, end, infileName); + + // Free the graph obtained for integrity checking. + gp_Free(&origGraph); + } + + // Report an error, if there was one, free the graph, and return + if (Result != OK && Result != NONEMBEDDABLE) + { + ErrorMessage("AN ERROR HAS BEEN DETECTED\n"); + Result = NOTOK; + // gp_Write(theGraph, "debug.after.txt", WRITE_DEBUGINFO); + } + + // Provide the output file(s) + else + { + // Restore the vertex ordering of the original graph (undo DFS numbering) + if (strchr(GetAlgorithmChoices(), command)) + gp_SortVertices(theGraph); + + // Determine the name of the primary output file + outfileName = ConstructPrimaryOutputFilename(infileName, outfileName, command); + + // For some algorithms, the primary output file is not always written + if ((strchr("pdo", command) && Result == NONEMBEDDABLE) || + (strchr("234", command) && Result == OK)) + { + // Do not write the file + } + + // Write the primary output file, if appropriate to do so + else + { + int writeResult = OK; + + if (pOutputStr == NULL) + writeResult = gp_Write(theGraph, outfileName, WRITE_ADJLIST); + else + writeResult = gp_WriteToString(theGraph, pOutputStr, WRITE_ADJLIST); + + if (writeResult != OK) + Result = NOTOK; + } + + // NOW WE WANT TO WRITE THE SECONDARY OUTPUT to a FILE or STRING + + // When called from the menu system, we want to write the planar or outerplanar + // obstruction, if one exists. For planar graph drawing, we want the character + // art rendition. + if (outfile2Name != NULL || pOutput2Str != NULL) + { + int writeResult = OK; + + if (pOutput2Str != NULL) + { + // A non-embeddable obstruction subgraph also goes into the primary output, not the secondary + if ((command == 'p' || command == 'o') && Result == NONEMBEDDABLE) + writeResult = gp_WriteToString(theGraph, pOutputStr, WRITE_ADJLIST); + + // Only the planar visibility representation goes into the secondary output + else if (command == 'd' && Result == OK) + writeResult = gp_DrawPlanar_RenderToString(theGraph, pOutput2Str); + } + else if (outfile2Name != NULL) + { + if ((command == 'p' || command == 'o') && Result == NONEMBEDDABLE) + { + // By default, use the same name as the primary output filename + if (strlen(outfile2Name) == 0) + outfile2Name = outfileName; + writeResult = gp_Write(theGraph, outfile2Name, WRITE_ADJLIST); + } + else if (command == 'd' && Result == OK) + { + // An empty but non-NULL string is passed to indicate the necessity + // of selecting a default name for the second output file. + // By default, add ".render.txt" to the primary output filename + if (strlen(outfile2Name) == 0) + strcat((outfile2Name = outfileName), ".render.txt"); + writeResult = gp_DrawPlanar_RenderToFile(theGraph, outfile2Name); + } + } + + if (writeResult != OK) + Result = NOTOK; + } + } + + // Free the graph + gp_Free(&theGraph); + + // Flush any remaining message content to the user, and return the result + FlushConsole(stdout); + return Result; +} + +/**************************************************************************** + WriteAlgorithmResults() + ****************************************************************************/ + +void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName) +{ + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + int charsAvailForStr = 0; + + if (infileName) + { + messageFormat = "The graph \"%.*s\" "; + charsAvailForStr = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForStr, infileName); + } + else + sprintf(messageContents, "The graph "); + Message(messageContents); + + switch (command) + { + case 'p': + sprintf(messageContents, "is%s planar.\n", Result == OK ? "" : " not"); + break; + case 'd': + sprintf(messageContents, "is%s planar.\n", Result == OK ? "" : " not"); + break; + case 'o': + sprintf(messageContents, "is%s outerplanar.\n", Result == OK ? "" : " not"); + break; + case '2': + sprintf(messageContents, "has %s subgraph homeomorphic to K_{2,3}.\n", Result == OK ? "no" : "a"); + break; + case '3': + sprintf(messageContents, "has %s subgraph homeomorphic to K_{3,3}.\n", Result == OK ? "no" : "a"); + break; + case '4': + sprintf(messageContents, "has %s subgraph homeomorphic to K_4.\n", Result == OK ? "no" : "a"); + break; + default: + sprintf(messageContents, "has not been processed due to unrecognized command.\n"); + break; + } + Message(messageContents); + + sprintf(messageContents, "Algorithm '%s' executed in %.3lf seconds.\n", + GetAlgorithmName(command), platform_GetDuration(start, end)); + Message(messageContents); +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTestAllGraphs.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTestAllGraphs.c new file mode 100644 index 000000000..9274780f6 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTestAllGraphs.c @@ -0,0 +1,391 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +typedef struct +{ + double duration; + int numGraphsRead; + int numOK; + int numNONEMBEDDABLE; + int errorFlag; +} testAllStats; + +typedef testAllStats *testAllStatsP; + +int testAllGraphs(graphP theGraph, char command, strOrFileP inputContainer, testAllStatsP stats); +int outputTestAllGraphsResults(char command, testAllStatsP stats, char *infileName, char *outfileName, char **outputStr); + +/**************************************************************************** + TestAllGraphs() + commandString - command to run; e.g.`-(pdo234)` to perform the corresponding algorithm on each graph in + a .g6 file + infileName - name of file to read, or NULL to cause the program to prompt the user for a filename + outfileName - name of primary output file, or NULL to construct an output filename based on the input + outputStr - pointer to string which we wish to use to store the transformation output + ****************************************************************************/ +int TestAllGraphs(char *commandString, char *infileName, char *outfileName, char **outputStr) +{ + int Result = OK; + platform_time start, end; + + int charsAvailForFilename = 0; + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + messageContents[MAXLINE] = '\0'; + + graphP theGraph; + + // Create the graph and, if needed, attach the correct algorithm to it + theGraph = gp_New(); + + if (commandString[0] == '-') + { + if (strchr(GetAlgorithmChoices(), commandString[1])) + { + if (infileName == NULL) + { + ErrorMessage("No input file provided.\n"); + Result = NOTOK; + } + else + { + messageFormat = "Start testing all graphs in \"%.*s\".\n"; + charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForFilename, infileName); + Message(messageContents); + + // Start the timer + platform_GetTime(start); + + strOrFileP inputContainer = sf_New(NULL, infileName, READTEXT); + if (inputContainer == NULL) + { + messageFormat = "Unable to open file \"%.*s\" for input.\n"; + charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForFilename, infileName); + ErrorMessage(messageContents); + + gp_Free(&theGraph); + + return NOTOK; + } + + testAllStats stats; + memset(&stats, 0, sizeof(testAllStats)); + + char command = commandString[1]; + Result = testAllGraphs(theGraph, command, inputContainer, &stats); + + // Stop the timer + platform_GetTime(end); + stats.duration = platform_GetDuration(start, end); + + if (Result != OK && Result != NONEMBEDDABLE) + { + messageFormat = "\nEncountered error while running command '%c' on all graphs in \"%.*s\".\n"; + charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, command, charsAvailForFilename, infileName); + ErrorMessage(messageContents); + } + else + { + sprintf(messageContents, "\nDone testing all graphs (%.3lf seconds).\n", stats.duration); + Message(messageContents); + } + + if (outputTestAllGraphsResults(command, &stats, infileName, outfileName, outputStr) != OK) + { + messageFormat = "Error outputting results running command '%c' on all graphs in \"%.*s\".\n"; + charsAvailForFilename = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, command, charsAvailForFilename, infileName); + ErrorMessage(messageContents); + Result = NOTOK; + } + } + } + else + { + ErrorMessage("Invalid argument; only -(pdo234) is allowed.\n"); + Result = NOTOK; + } + } + else + { + ErrorMessage("Invalid argument; must start with '-'.\n"); + Result = NOTOK; + } + + gp_Free(&theGraph); + return Result; +} + +int testAllGraphs(graphP theGraph, char command, strOrFileP inputContainer, testAllStatsP stats) +{ + int Result = OK; + + char *messageFormat = NULL; + char messageContents[MAXLINE + 1]; + messageContents[MAXLINE] = '\0'; + + graphP copyOfOrigGraph = NULL; + int embedFlags = GetEmbedFlags(command); + int numOK = 0, numNONEMBEDDABLE = 0, errorFlag = FALSE; + + G6ReadIterator *pG6ReadIterator = NULL; + Result = allocateG6ReadIterator(&pG6ReadIterator, theGraph); + + if (Result != OK) + { + ErrorMessage("Unable to allocate G6ReadIterator.\n"); + stats->errorFlag = TRUE; + return Result; + } + + Result = beginG6ReadIterationFromG6StrOrFile(pG6ReadIterator, inputContainer); + + if (Result != OK) + { + ErrorMessage("Unable to begin .g6 read iteration.\n"); + freeG6ReadIterator(&pG6ReadIterator); + stats->errorFlag = TRUE; + return Result; + } + + int graphOrder = pG6ReadIterator->graphOrder; + // We have to set the maximum arc capacity (i.e. (N * (N - 1))) because some of the test files + // can contain complete graphs, and the graph drawing, K_{3, 3} search, and K_4 search extensions + // don't support expanding the arc capacity after being attached. + if (strchr("d34", command) != NULL) + { + Result = gp_EnsureArcCapacity(pG6ReadIterator->currGraph, (graphOrder * (graphOrder - 1))); + if (Result != OK) + { + ErrorMessage("Unable to maximize arc capacity of G6ReadIterator's graph struct.\n"); + freeG6ReadIterator(&pG6ReadIterator); + stats->errorFlag = TRUE; + return Result; + } + } + + AttachAlgorithm(pG6ReadIterator->currGraph, command); + + copyOfOrigGraph = gp_New(); + if (copyOfOrigGraph == NULL) + { + ErrorMessage("Unable to allocate graph to store copy of original graph before embedding.\n"); + stats->errorFlag = TRUE; + return NOTOK; + } + + Result = gp_InitGraph(copyOfOrigGraph, graphOrder); + if (Result != OK) + { + ErrorMessage("Unable to initialize graph datastructure to store copy of original graph before embedding.\n"); + gp_Free(©OfOrigGraph); + freeG6ReadIterator(&pG6ReadIterator); + stats->errorFlag = TRUE; + return Result; + } + + if (strchr("d34", command) != NULL) + { + Result = gp_EnsureArcCapacity(copyOfOrigGraph, (graphOrder * (graphOrder - 1))); + if (Result != OK) + { + ErrorMessage("Unable to maximize arc capacity of graph struct to contain copy of original graph.\n"); + gp_Free(©OfOrigGraph); + freeG6ReadIterator(&pG6ReadIterator); + stats->errorFlag = TRUE; + return Result; + } + } + + while (true) + { + Result = readGraphUsingG6ReadIterator(pG6ReadIterator); + + if (Result != OK) + { + messageFormat = "Unable to read graph on line %d from .g6 read iterator.\n"; + sprintf(messageContents, messageFormat, pG6ReadIterator->numGraphsRead + 1); + ErrorMessage(messageContents); + errorFlag = TRUE; + break; + } + + if (pG6ReadIterator->currGraph == NULL) + break; + + gp_CopyGraph(copyOfOrigGraph, pG6ReadIterator->currGraph); + + Result = gp_Embed(pG6ReadIterator->currGraph, embedFlags); + + if (gp_TestEmbedResultIntegrity(pG6ReadIterator->currGraph, copyOfOrigGraph, Result) != Result) + Result = NOTOK; + + if (Result == OK) + numOK++; + else if (Result == NONEMBEDDABLE) + numNONEMBEDDABLE++; + else + { + messageFormat = "Error applying algorithm '%c' to graph on line %d.\n"; + sprintf(messageContents, messageFormat, command, pG6ReadIterator->numGraphsRead + 1); + ErrorMessage(messageContents); + errorFlag = TRUE; + break; + } + + gp_ReinitializeGraph(copyOfOrigGraph); + } + + stats->numGraphsRead = pG6ReadIterator->numGraphsRead; + stats->numOK = numOK; + stats->numNONEMBEDDABLE = numNONEMBEDDABLE; + stats->errorFlag = errorFlag; + + if (endG6ReadIteration(pG6ReadIterator) != OK) + ErrorMessage("Unable to end G6ReadIterator.\n"); + + if (freeG6ReadIterator(&pG6ReadIterator) != OK) + ErrorMessage("Unable to free G6ReadIterator.\n"); + + gp_Free(©OfOrigGraph); + + return Result; +} + +int outputTestAllGraphsResults(char command, testAllStatsP stats, char *infileName, char *outfileName, char **outputStr) +{ + int Result = OK; + + char *finalSlash = strrchr(infileName, FILE_DELIMITER); + char *infileBasename = finalSlash ? (finalSlash + 1) : infileName; + + char *headerFormat = "FILENAME=\"%s\" DURATION=\"%.3lf\"\n"; + char *headerStr = (char *)malloc( + ( + strlen(headerFormat) + + strlen(infileBasename) + + strlen("-1.7976931348623158e+308") + // -DBL_MAX from float.h + 3) * + sizeof(char)); + if (headerStr == NULL) + { + ErrorMessage("Unable allocate memory for output file header.\n"); + return NOTOK; + } + + sprintf(headerStr, headerFormat, infileBasename, stats->duration); + int numCharsToReprNumGraphsRead = 0, numCharsToReprNumOK = 0, numCharsToReprNumNONEMBEDDABLE = 0; + if (GetNumCharsToReprInt(stats->numGraphsRead, &numCharsToReprNumGraphsRead) != OK || + GetNumCharsToReprInt(stats->numOK, &numCharsToReprNumOK) != OK || + GetNumCharsToReprInt(stats->numNONEMBEDDABLE, &numCharsToReprNumNONEMBEDDABLE) != OK) + { + ErrorMessage("Unable to determine the number of characters required to represent testAllGraphs stat values.\n"); + + free(headerStr); + headerStr = NULL; + + return NOTOK; + } + + char *resultsStr = (char *)malloc( + ( + 3 + numCharsToReprNumGraphsRead + + 1 + numCharsToReprNumOK + + 1 + numCharsToReprNumNONEMBEDDABLE + + 1 + 8 + // either ERROR or SUCCESS, so the longer of which is 7 + 1 chars + 3) * + sizeof(char)); + if (resultsStr == NULL) + { + ErrorMessage("Unable allocate memory for results string.\n"); + + free(headerStr); + headerStr = NULL; + + return NOTOK; + } + + sprintf(resultsStr, "-%c %d %d %d %s\n", + command, stats->numGraphsRead, stats->numOK, stats->numNONEMBEDDABLE, stats->errorFlag ? "ERROR" : "SUCCESS"); + + strOrFileP testOutput = NULL; + + if (outfileName != NULL) + { + testOutput = sf_New(NULL, outfileName, WRITETEXT); + } + else + { + if (outputStr == NULL) + { + ErrorMessage("Both outfileName and pointer to outputStr are NULL.\n"); + } + else + { + if ((*outputStr) != NULL) + ErrorMessage("Expected memory to which outputStr points to be NULL.\n"); + else + { + testOutput = sf_New(NULL, NULL, WRITETEXT); + } + } + } + + if (testOutput == NULL) + { + ErrorMessage("Unable to set up string-or-file container for test output.\n"); + + free(headerStr); + headerStr = NULL; + + free(resultsStr); + resultsStr = NULL; + + if (outputStr != NULL && (*outputStr) != NULL) + { + free((*outputStr)); + (*outputStr) = NULL; + } + + return NOTOK; + } + + if (sf_fputs(headerStr, testOutput) < 0) + { + ErrorMessage("Unable to output headerStr to testOutput.\n"); + Result = NOTOK; + } + + if (Result == OK) + { + if (sf_fputs(resultsStr, testOutput) < 0) + { + ErrorMessage("Unable to output resultsStr to testOutput.\n"); + Result = NOTOK; + } + } + + if (Result == OK) + { + if (outputStr != NULL) + (*outputStr) = sf_takeTheStr(testOutput); + } + + free(headerStr); + headerStr = NULL; + + free(resultsStr); + resultsStr = NULL; + + sf_Free(&testOutput); + + return Result; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTransformGraph.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTransformGraph.c new file mode 100644 index 000000000..084a1d477 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityTransformGraph.c @@ -0,0 +1,102 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +int transformFile(graphP theGraph, char *infileName); +int transformString(graphP theGraph, char *inputStr); + +/**************************************************************************** + TransformGraph() + commandString - command to run; i.e. `-(gam)` to transform graph to .g6, adjacency list, or + adjacency matrix format + infileName - name of file to read, or NULL to cause the program to prompt the user for a filename + inputStr - string containing input graph, or NULL to cause the program to fall back on reading from file + outputBase - pointer to the flag set for whether output is 0- or 1-based + outputFormat - output format + outfileName - name of primary output file, or NULL to construct an output filename based on the input + outputStr - pointer to string which we wish to use to store the transformation output + ****************************************************************************/ +int TransformGraph(char *commandString, char *infileName, char *inputStr, int *outputBase, char *outfileName, char **outputStr) +{ + int Result = OK; + + graphP theGraph; + + theGraph = gp_New(); + + int outputFormat = -1; + + if (commandString[0] == '-') + { + if (commandString[1] == 'g') + outputFormat = WRITE_G6; + else if (commandString[1] == 'a') + outputFormat = WRITE_ADJLIST; + else if (commandString[1] == 'm') + outputFormat = WRITE_ADJMATRIX; + else + { + ErrorMessage("Invalid argument; only -(gam) is allowed.\n"); + return NOTOK; + } + + if (inputStr) + Result = transformString(theGraph, inputStr); + else + Result = transformFile(theGraph, infileName); + + if (Result != OK) + { + ErrorMessage("Unable to transform input graph.\n"); + } + else + { + // Want to know whether the output is 0- or 1-based; will always be + // 0-based for transformations of .g6 input + if (outputBase != NULL) + (*outputBase) = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? 1 : 0; + + if (outputStr != NULL) + Result = gp_WriteToString(theGraph, outputStr, outputFormat); + else + Result = gp_Write(theGraph, outfileName, outputFormat); + + if (Result != OK) + ErrorMessage("Unable to write graph.\n"); + } + } + else + { + ErrorMessage("Invalid argument; must start with '-'.\n"); + Result = NOTOK; + } + + gp_Free(&theGraph); + return Result; +} + +int transformFile(graphP theGraph, char *infileName) +{ + if (infileName == NULL) + { + if ((infileName = ConstructInputFilename(infileName)) == NULL) + return NOTOK; + } + + return gp_Read(theGraph, infileName); +} + +int transformString(graphP theGraph, char *inputStr) +{ + if (inputStr == NULL || strlen(inputStr) == 0) + { + ErrorMessage("Input string is null or empty.\n"); + return NOTOK; + } + + return gp_ReadFromString(theGraph, inputStr); +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityUtils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityUtils.c new file mode 100644 index 000000000..c14b6c3e9 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/planarityApp/planarityUtils.c @@ -0,0 +1,666 @@ +/* +Copyright (c) 1997-2025, John M. Boyer +All rights reserved. +See the LICENSE.TXT file for licensing information. +*/ + +#include "planarity.h" + +/**************************************************************************** + Configuration + ****************************************************************************/ + +char Mode = 'r', + OrigOut = 'n', + OrigOutFormat = 'a', + EmbeddableOut = 'n', + ObstructedOut = 'n', + AdjListsForEmbeddingsOut = 'n'; + +void Reconfigure(void) +{ + fflush(stdin); + + Prompt("\nDo you want to \n" + " Randomly generate graphs (r),\n" + " Specify a graph (s),\n" + " Randomly generate a maximal planar graph (m), or\n" + " Randomly generate a non-planar graph (n)? "); + scanf(" %c", &Mode); + + Mode = (char)tolower(Mode); + if (!strchr("rsmn", Mode)) + Mode = 's'; + + if (Mode == 'r') + { + Message("\nNOTE: The directories for the graphs you want must exist.\n\n"); + + Prompt("Do you want original graphs in directory 'random'? (y/n) "); + scanf(" %c", &OrigOut); + + if (tolower(OrigOut) == 'y') + { + Prompt("Do you want to output generated graphs to Adjacency List (last 10 only) or to G6 (all)? (a/g) "); + scanf(" %c", &OrigOutFormat); + } + + Prompt("Do you want adj. matrix of embeddable graphs in directory 'embedded' (last 10 max))? (y/n) "); + scanf(" %c", &EmbeddableOut); + + Prompt("Do you want adj. matrix of obstructed graphs in directory 'obstructed' (last 10 max)? (y/n) "); + scanf(" %c", &ObstructedOut); + + Prompt("Do you want adjacency list format of embeddings in directory 'adjlist' (last 10 max)? (y/n) "); + scanf(" %c", &AdjListsForEmbeddingsOut); + } + + FlushConsole(stdout); +} + +void FlushConsole(FILE *f) +{ + fflush(f); +} + +void Prompt(char *message) +{ + Message(message); + FlushConsole(stdout); +} + +/**************************************************************************** + ****************************************************************************/ + +void SaveAsciiGraph(graphP theGraph, char *filename) +{ + int e, EsizeOccupied, vertexLabelFix; + FILE *outfile = fopen(filename, WRITETEXT); + + // The filename may specify a directory that doesn't exist + if (outfile == NULL) + { + char messageContents[MAXLINE + 1]; + char *messageFormat = "Failed to write to \"%.*s\"\nMake the directory if not present\n"; + int charsAvailForStrToInclude = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForStrToInclude, filename); + ErrorMessage(messageContents); + return; + } + + // If filename includes path elements, remove them before writing the file's name to the file + if (strrchr(filename, FILE_DELIMITER)) + filename = strrchr(filename, FILE_DELIMITER) + 1; + + fprintf(outfile, "%s\n", filename); + + // This edge list file format uses 1-based vertex numbering, and the current code + // internally uses 1-based indexing by default, so this vertex label 'fix' adds zero + // But earlier code used 0-based indexing and added one on output, so we replicate + // that behavior in case the current code has been compiled with zero-based indexing. + vertexLabelFix = 1 - gp_GetFirstVertex(theGraph); + + // Iterate over the edges of the graph + EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); + for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e += 2) + { + // Only output edges that haven't been deleted (i.e. skip the edge holes) + if (gp_EdgeInUse(theGraph, e)) + { + fprintf(outfile, "%d %d\n", + gp_GetNeighbor(theGraph, e) + vertexLabelFix, + gp_GetNeighbor(theGraph, e + 1) + vertexLabelFix); + } + } + + // Since vertex numbers are at least 1, this indicates the end of the edge list + fprintf(outfile, "0 0\n"); + + fclose(outfile); +} + +/**************************************************************************** + ReadTextFileIntoString() + Reads the file content indicated by infileName using a single fread(), and + returns the result in an allocated string. The caller needs to free() the + returned string when done with it. + Returns NULL on error, or an allocated string containing the file content. + ****************************************************************************/ + +char *ReadTextFileIntoString(char *infileName) +{ + FILE *infile = NULL; + char *inputString = NULL; + + if ((infile = fopen(infileName, "r")) == NULL) + ErrorMessage("fopen() failed.\n"); + else + { + long filePos = ftell(infile); + long fileSize; + + fseek(infile, 0, SEEK_END); + fileSize = ftell(infile); + fseek(infile, filePos, SEEK_SET); + + if ((inputString = (char *)malloc((fileSize + 1) * sizeof(char))) != NULL) + { + long bytesRead = fread((void *)inputString, 1, fileSize, infile); + inputString[bytesRead] = '\0'; + } + + fclose(infile); + } + + return inputString; +} + +/**************************************************************************** + * TextFileMatchesString() + * + * Compares the text file content from the file named 'theFilename' with + * the content of 'theString'. + * + * Textual equality is measured as content equality except for suppressing + * differences between CRLF and LF-only line delimiters. + * + * Returns TRUE if the contents are textually equal, FALSE otherwise + ****************************************************************************/ + +int TextFileMatchesString(char *theFilename, char *theString) +{ + FILE *infile = NULL; + int Result = TRUE; + + if (theFilename != NULL) + infile = fopen(theFilename, "r"); + + if (infile == NULL) + Result = FALSE; + else + { + int c1 = 0, c2 = 0, stringIndex = 0; + + // Read the input file to the end + while ((c1 = fgetc(infile)) != EOF) + { + // Want to suppress distinction between lines ending with CRLF versus LF + // by looking at only LF characters in the file + if (c1 == '\r') + continue; + + // Since c1 now has a non-CR, non-EOF from the input file, we now also + // get a character from the string, except ignoring CRs again + while ((c2 = (int)theString[stringIndex++]) == '\r') + ; + + // If c1 doesn't equal c2 (whether c2 is a null terminator or a different character) + // then the file content doesn't match the string + if (c1 != c2) + { + Result = FALSE; + break; + } + } + + // If the outer while loop got to the end of the file + if (c1 == EOF) + { + // Then get another character from the string, once again suppressing CRs, and then... + while ((c2 = (int)theString[stringIndex++]) == '\r') + ; + // Test whether or not the second file also ends, same as the first. + if (c2 != '\0') + Result = FALSE; + } + } + + if (infile != NULL) + fclose(infile); + return Result; +} + +/**************************************************************************** + ****************************************************************************/ + +int TextFilesEqual(char *file1Name, char *file2Name) +{ + FILE *infile1 = NULL, *infile2 = NULL; + int Result = TRUE; + + infile1 = fopen(file1Name, "r"); + infile2 = fopen(file2Name, "r"); + + if (infile1 == NULL || infile2 == NULL) + Result = FALSE; + else + { + int c1 = 0, c2 = 0; + + // Read the first file to the end + while ((c1 = fgetc(infile1)) != EOF) + { + // Want to suppress distinction between lines ending with CRLF versus LF + if (c1 == '\r') + continue; + + // Get a char from the second file, except suppress CR again + while ((c2 = fgetc(infile2)) == '\r') + ; + + // If we got a char from the first file, but not from the second + // then the second file is shorter, so files are not equal + if (c2 == EOF) + { + Result = FALSE; + break; + } + + // If we got a char from second file, but not equal to char from + // first file, then files are not equal + if (c1 != c2) + { + Result = FALSE; + break; + } + } + + // If we got to the end of the first file without breaking the loop... + if (c1 == EOF) + { + // Then, once again, suppress CRs first, and then... + while ((c2 = fgetc(infile2)) == '\r') + ; + // Test whether or not the second file also ends, same as the first. + if (fgetc(infile2) != EOF) + Result = FALSE; + } + } + + if (infile1 != NULL) + fclose(infile1); + if (infile2 != NULL) + fclose(infile2); + return Result; +} + +/**************************************************************************** + ****************************************************************************/ + +int BinaryFilesEqual(char *file1Name, char *file2Name) +{ + FILE *infile1 = NULL, *infile2 = NULL; + int Result = TRUE; + + infile1 = fopen(file1Name, "r"); + infile2 = fopen(file2Name, "r"); + + if (infile1 == NULL || infile2 == NULL) + Result = FALSE; + else + { + int c1 = 0, c2 = 0; + + // Read the first file to the end + while ((c1 = fgetc(infile1)) != EOF) + { + // If we got a char from the first file, but not from the second + // then the second file is shorter, so files are not equal + if ((c2 = fgetc(infile2)) == EOF) + { + Result = FALSE; + break; + } + + // If we got a char from second file, but not equal to char from + // first file, then files are not equal + if (c1 != c2) + { + Result = FALSE; + break; + } + } + + // If we got to the end of the first file without breaking the loop... + if (c1 == EOF) + { + // Then attempt to read from the second file to ensure it also ends. + if (fgetc(infile2) != EOF) + Result = FALSE; + } + } + + if (infile1 != NULL) + fclose(infile1); + if (infile2 != NULL) + fclose(infile2); + return Result; +} + +/**************************************************************************** + ALGORITHM FLAGS/SPECIFIERS +****************************************************************************/ + +char *GetAlgorithmFlags(void) +{ + return "C = command (algorithm implementation to run)\n" + " -p = Planar embedding and Kuratowski subgraph isolation\n" + " -d = Planar graph drawing by visibility representation\n" + " -o = Outerplanar embedding and obstruction isolation\n" + " -2 = Search for subgraph homeomorphic to K_{2,3}\n" + " -3 = Search for subgraph homeomorphic to K_{3,3}\n" + " -4 = Search for subgraph homeomorphic to K_4\n" + "\n"; +} + +char *GetAlgorithmSpecifiers(void) +{ + return "P. Planar embedding and Kuratowski subgraph isolation\n" + "D. Planar graph drawing by visibility representation\n" + "O. Outerplanar embedding and obstruction isolation\n" + "2. Search for subgraph homeomorphic to K_{2,3}\n" + "3. Search for subgraph homeomorphic to K_{3,3}\n" + "4. Search for subgraph homeomorphic to K_4\n"; +} + +char *GetAlgorithmChoices(void) +{ + return "pdo234"; +} + +/**************************************************************************** + ****************************************************************************/ + +int GetEmbedFlags(char command) +{ + int embedFlags = 0; + + switch (command) + { + case 'o': + embedFlags = EMBEDFLAGS_OUTERPLANAR; + break; + case 'p': + embedFlags = EMBEDFLAGS_PLANAR; + break; + case 'd': + embedFlags = EMBEDFLAGS_DRAWPLANAR; + break; + case '2': + embedFlags = EMBEDFLAGS_SEARCHFORK23; + break; + case '3': + embedFlags = EMBEDFLAGS_SEARCHFORK33; + break; + case '4': + embedFlags = EMBEDFLAGS_SEARCHFORK4; + break; + } + + return embedFlags; +} + +/**************************************************************************** + ****************************************************************************/ + +char *GetAlgorithmName(char command) +{ + char *algorithmName = "UnsupportedAlgorithm"; + + switch (command) + { + case 'p': + algorithmName = "PlanarEmbed"; + break; + case 'd': + algorithmName = DRAWPLANAR_NAME; + break; + case 'o': + algorithmName = "OuterplanarEmbed"; + break; + case '2': + algorithmName = K23SEARCH_NAME; + break; + case '3': + algorithmName = K33SEARCH_NAME; + break; + case '4': + algorithmName = K4SEARCH_NAME; + break; + } + + return algorithmName; +} + +/**************************************************************************** + ****************************************************************************/ + +char *GetTransformationName(char command) +{ + char *transformationName = "UnsupportedTransformation"; + + switch (command) + { + case 'g': + transformationName = "G6"; + break; + case 'a': + transformationName = "AdjList"; + break; + case 'm': + transformationName = "AdjMat"; + break; + } + + return transformationName; +} + +char *GetSupportedOutputChoices(void) +{ + return "G. G6 format\n" + "A. Adjacency List format\n" + "M. Adjacency Matrix format\n"; +} + +char *GetSupportedOutputFormats(void) +{ + return "gam"; +} + +/**************************************************************************** + ****************************************************************************/ + +char *GetBaseName(int baseFlag) +{ + char *transformationName = baseFlag ? "1-based" : "0-based"; + + return transformationName; +} + +/**************************************************************************** + ****************************************************************************/ + +void AttachAlgorithm(graphP theGraph, char command) +{ + switch (command) + { + case 'd': + gp_AttachDrawPlanar(theGraph); + break; + case '2': + gp_AttachK23Search(theGraph); + break; + case '3': + gp_AttachK33Search(theGraph); + break; + case '4': + gp_AttachK4Search(theGraph); + break; + } +} + +/**************************************************************************** + A string used to construct input and output filenames. + + The SUFFIXMAXLENGTH is 32 to accommodate ".out.txt" + ".render.txt" + ".test.txt" + ****************************************************************************/ + +#define FILENAMEMAXLENGTH 128 +#define ALGORITHMNAMEMAXLENGTH 32 +#define SUFFIXMAXLENGTH 32 + +char theFileName[FILENAMEMAXLENGTH + 1 + ALGORITHMNAMEMAXLENGTH + 1 + SUFFIXMAXLENGTH + 1]; + +/**************************************************************************** + ConstructInputFilename() + Returns a string not owned by the caller (do not free string). + String contains infileName content if infileName is non-NULL. + If infileName is NULL, then the user is asked to supply a name. + Returns NULL on error, or a non-NULL string on success. + ****************************************************************************/ + +char *ConstructInputFilename(char *infileName) +{ + if (infileName == NULL) + { + do + { + Prompt("Enter graph file name: "); + fflush(stdin); + scanf(" %s", theFileName); + + if (strncmp(theFileName, "stdin", strlen("stdin")) != 0 && !strchr(theFileName, '.')) + { + Message("Graph file name does not have extension; automatically appending \".txt\".\n"); + strcat(theFileName, ".txt"); + } + } while (strlen(theFileName) == 0); + } + else + { + if (strlen(infileName) > FILENAMEMAXLENGTH) + { + ErrorMessage("Filename is too long"); + return NULL; + } + else if (strlen(infileName) == 0) + { + ErrorMessage("Filename is empty"); + return NULL; + } + strcpy(theFileName, infileName); + } + + return theFileName; +} + +/**************************************************************************** + ConstructPrimaryOutputFilename() + Returns a string not owned by the caller (do not free string). + Reuses the same memory space as ConstructInputFilename(). + If outfileName is non-NULL, then the result string contains its content. + If outfileName is NULL, then the infileName and the command's algorithm name + are used to construct a string. + Returns non-NULL string + ****************************************************************************/ + +char *ConstructPrimaryOutputFilename(char *infileName, char *outfileName, char command) +{ + char *algorithmName = GetAlgorithmName(command); + + if (outfileName == NULL) + { + // The output filename is based on the input filename + if (theFileName != infileName) + strcpy(theFileName, infileName); + + // If the primary output filename has not been given, then we use + // the input filename + the algorithm name + a simple suffix + if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH) + { + strcat(theFileName, "."); + strcat(theFileName, algorithmName); + } + else + ErrorMessage("Algorithm Name is too long, so it will not be used in output filename."); + + strcat(theFileName, ".out.txt"); + } + else + { + if (strlen(outfileName) > FILENAMEMAXLENGTH) + { + // The output filename is based on the input filename + if (theFileName != infileName) + strcpy(theFileName, infileName); + + if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH) + { + strcat(theFileName, "."); + strcat(theFileName, algorithmName); + } + strcat(theFileName, ".out.txt"); + char messageContents[MAXLINE + 1]; + char *messageFormat = "Outfile filename is too long. Result placed in \"%.*s\""; + int charsAvailForStrToInclude = (int)(MAXLINE - strlen(messageFormat)); + sprintf(messageContents, messageFormat, charsAvailForStrToInclude, theFileName); + ErrorMessage(messageContents); + } + else + { + if (theFileName != outfileName) + strcpy(theFileName, outfileName); + } + } + + return theFileName; +} + +/**************************************************************************** + ConstructTransformationExpectedResultFilename() + Returns a string whose ownership will be transferred to the caller (must free string). + If outfileName is non-NULL, then the result string contains its content. + If outfileName is NULL, then the infileName, the command's algorithm name, and + whether or not this output file correspond to the actual (0) or expected (1) + output file from testing are used to construct a string. + Returns non-NULL string + ****************************************************************************/ + +int ConstructTransformationExpectedResultFilename(char *infileName, char **outfileName, char command, int baseFlag) +{ + int Result = OK; + char *baseName = GetBaseName(baseFlag); + char *transformationName = GetTransformationName(command); + int infileNameLen = -1; + + if (infileName == NULL || (infileNameLen = strlen(infileName)) < 1) + { + ErrorMessage("Cannot construct transformation output filename for empty infileName.\n"); + return NOTOK; + } + + if ((*outfileName) == NULL) + { + (*outfileName) = (char *)calloc(infileNameLen + 1 + strlen(baseName) + 1 + strlen(transformationName) + ((command == 'g') ? strlen(".out.g6") : strlen(".out.txt")) + 1, sizeof(char)); + + if ((*outfileName) == NULL) + { + ErrorMessage("Unable to allocate memory for output filename.\n"); + return NOTOK; + } + + strcpy((*outfileName), infileName); + strcat((*outfileName), "."); + strcat((*outfileName), baseName); + strcat((*outfileName), "."); + strcat((*outfileName), transformationName); + strcat((*outfileName), command == 'g' ? ".out.g6" : ".out.txt"); + } + else + { + ErrorMessage("outfileName already allocated.\n"); + Result = NOTOK; + } + + return Result; +} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/Makefile.am b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/Makefile.am new file mode 100644 index 000000000..8f931e96c --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/Makefile.am @@ -0,0 +1,46 @@ +samplesdir = @docdir@/samples + +dist_samples_DATA = \ + drawExample.0-based.txt \ + drawExample.0-based.txt.ColorVertices.out.txt \ + drawExample.0-based.txt.DrawPlanar.out.txt \ + drawExample.0-based.txt.DrawPlanar.out.txt.render.txt \ + drawExample.txt \ + drawExample.txt.ColorVertices.out.txt \ + drawExample.txt.DrawPlanar.out.txt \ + drawExample.txt.DrawPlanar.out.txt.render.txt \ + K10.g6 \ + K10.g6.0-based.AdjList.out.txt \ + K10.g6.0-based.AdjList.out.txt.0-based.G6.out.g6 \ + K10.g6.0-based.AdjMat.out.txt \ + maxPlanar5.0-based.txt \ + maxPlanar5.0-based.txt.ColorVertices.out.txt \ + maxPlanar5.0-based.txt.DrawPlanar.out.txt \ + maxPlanar5.0-based.txt.DrawPlanar.out.txt.render.txt \ + maxPlanar5.0-based.txt.PlanarEmbed.out.txt \ + maxPlanar5.txt \ + maxPlanar5.txt.ColorVertices.out.txt \ + maxPlanar5.txt.DrawPlanar.out.txt \ + maxPlanar5.txt.DrawPlanar.out.txt.render.txt \ + maxPlanar5.txt.PlanarEmbed.out.txt \ + N5-all.g6 \ + N5-all.g6.0-based.AdjList.out.txt \ + N5-all.g6.0-based.AdjMat.out.txt \ + nauty_example.g6 \ + nauty_example.g6.0-based.AdjList.out.txt \ + nauty_example.g6.0-based.AdjList.out.txt.0-based.G6.out.g6 \ + nauty_example.g6.0-based.AdjMat.out.txt \ + Petersen.0-based.txt \ + Petersen.0-based.txt.ColorVertices.out.txt \ + Petersen.0-based.txt.K23Search.out.txt \ + Petersen.0-based.txt.K33Search.out.txt \ + Petersen.0-based.txt.K4Search.out.txt \ + Petersen.0-based.txt.OuterplanarEmbed.out.txt \ + Petersen.0-based.txt.PlanarEmbed.out.txt \ + Petersen.txt \ + Petersen.txt.ColorVertices.out.txt \ + Petersen.txt.K23Search.out.txt \ + Petersen.txt.K33Search.out.txt \ + Petersen.txt.K4Search.out.txt \ + Petersen.txt.OuterplanarEmbed.out.txt \ + Petersen.txt.PlanarEmbed.out.txt \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/nauty_example.LEDA b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/nauty_example.LEDA new file mode 100644 index 000000000..9b4334c76 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/samples/nauty_example.LEDA @@ -0,0 +1,17 @@ +LEDA.GRAPH +void +void +-2 + +5 +|{v1}| +|{v2}| +|{v3}| +|{v4}| +|{v5}| + +4 +1 3 0 |{}| +1 5 0 |{}| +2 4 0 |{}| +4 5 0 |{}| diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/configure.ac b/extern/edge-addition-planarity-suite-Version_4.0.0.0/configure.ac new file mode 100644 index 000000000..801162f11 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/configure.ac @@ -0,0 +1,44 @@ +# The version number in the following line must be synchronized with +# the project version numbering in graphLib.h +AC_INIT([planarity],[4.0.0.0],[jboyer@acm.org]) +AM_INIT_AUTOMAKE([subdir-objects] [foreign]) +AC_CONFIG_MACRO_DIRS([m4]) +AC_CONFIG_SRCDIR([c/]) + +# The version of the libtool library is of the form current:revision:age +# +# See http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# +# The version number in the following line must be synchronized with +# the LibPlanarity version numbers in graphLib.h +# +# When doing a release, they should be updated like this: +# 1. If no interfaces changed, only implementations: just increment +# revision. +# 2. If interfaces were added, none removed: increment current, set +# revision to zero and increment age. +# 3. If interfaces were removed (breaks backward compatibility): increment +# current, and set both revision and age to zero. +LT_CURRENT=2 +LT_REVISION=0 +LT_AGE=0 +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) + +PKG_INSTALLDIR + +AC_PROG_CC +LT_INIT +AC_PROG_INSTALL + +AC_CHECK_HEADERS([ctype.h stdio.h stdlib.h string.h time.h unistd.h]) + +AC_CONFIG_FILES([ + Makefile + c/samples/Makefile +]) +AC_CONFIG_FILES([test-samples.sh], [chmod +x test-samples.sh]) +AC_CONFIG_FILES([libplanarity.pc]) + +AC_OUTPUT diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/launch.json b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/launch.json new file mode 100644 index 000000000..cd44be800 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/launch.json @@ -0,0 +1,229 @@ +{ + "configurations": [ + { + "name": "Python Debugger: Current File with Arguments", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "subProcess": true, + "justMyCode": false, + "args": [ + // Args to test test_table_generation_with_numInvalidOK.py + // "-n", + // "5,8", + // "-c", + // "3", + // "p", + // "o" + ] + }, + { + "name": "C/C++: (Windows) [Debug] gcc.exe build and debug planarity project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}\\Debug\\planarity.exe", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}\\c\\samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "C:\\msys64\\ucrt64\\bin\\gdb.exe", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "C/C++: (Windows) [Debug] gcc.exe build planarity project" + }, + { + "name": "C/C++: (Windows) [Release] gcc.exe build and run planarity project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}\\Release\\planarity.exe", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}\\c\\samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "C:\\msys64\\ucrt64\\bin\\gdb.exe", + "setupCommands": [], + "preLaunchTask": "C/C++: (Windows) [Release] gcc.exe build planarity project" + }, + { + "name": "C/C++: (Linux) [Debug] gcc build and debug planarity project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/Debug/planarity", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}/c/samples/nauty_example.g6", + // "${workspaceFolder}/c/samples/nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}/c/samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "C/C++: (Linux) [Debug] gcc build planarity project" + }, + { + "name": "C/C++: (Linux) [Release] gcc build and run planarity project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/Release/planarity", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}/c/samples/nauty_example.g6", + // "${workspaceFolder}/c/samples/nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}/c/samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [], + "preLaunchTask": "C/C++: (Linux) [Release] gcc build planarity project" + }, + { + "name": "C/C++: (Windows) [Debug] cl.exe build and debug planarity project", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}\\Debug\\planarity.exe", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}\\c\\samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "console": "integratedTerminal", + // "externalConsole": false, // externalConsole deprecated for MSVC + "preLaunchTask": "C/C++: (Windows) [Debug] cl.exe build planarity project" + }, + { + "name": "C/C++: (Windows) [Release] cl.exe build and run planarity project", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}\\Release\\planarity.exe", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6", + // "${workspaceFolder}\\c\\samples\\nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}\\c\\samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "console": "integratedTerminal", + // "externalConsole": false, // externalConsole deprecated for MSVC + "preLaunchTask": "C/C++: (Windows) [Release] cl.exe build planarity project" + }, + { + "name": "C/C++: (MacOS) [Debug] clang build and debug planarity project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/Debug/planarity", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}/c/samples/nauty_example.g6", + // "${workspaceFolder}/c/samples/nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}/c/samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "lldb", + "preLaunchTask": "C/C++: (MacOS) [Debug] clang build planarity project" + }, + { + "name": "C/C++: (MacOS) [Release] clang build and run planarity project", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/Release/planarity", + "args": [ + // Command: `planarity -x [-q] -a I O` + // "-x", + // "-a", + // "${workspaceFolder}/c/samples/nauty_example.g6", + // "${workspaceFolder}/c/samples/nauty_example.g6.AdjList.expected.out.txt" + // Command: `planarity -test ` + // "-test", + // "${workspaceFolder}/c/samples" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "lldb", + "preLaunchTask": "C/C++: (MacOS) [Release] clang build planarity project" + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/settings.json b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/settings.json new file mode 100644 index 000000000..26c8afde9 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "[python]": { + "editor.rulers": [ + 79, + 99 + ], + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.formatOnSave": true + }, + "editor.formatOnSave": true, + "editor.renderWhitespace": "all", + "files.autoSave": "afterDelay", + "python.analysis.typeCheckingMode": "basic", + "black-formatter.cwd": "${workspaceFolder}/TestSupport/planaritytesting", + "black-formatter.args": [ + "--line-length=79", + "--target-version=py312" + ], + "pylint.args": [ + "--rcfile=${workspaceFolder}/TestSupport/planaritytesting/.pylintrc" + ] +} \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/tasks.json b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/tasks.json new file mode 100644 index 000000000..2227c946f --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/.vscode/tasks.json @@ -0,0 +1,314 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: (Windows) [Debug] gcc.exe build planarity project", + "command": "C:\\msys64\\ucrt64\\bin\\gcc.exe", + "args": [ + "-fdiagnostics-color=always", + "-DDEBUG", // Added so that _DEBUG is defined; see appconst.h + //"-DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "-Wall", + // "-Wextra", + "-g", // Generate debug symbols + "${workspaceFolder}\\c\\planarityApp\\planarity.c", + "${workspaceFolder}\\c\\planarityApp\\planarityCommandLine.c", + "${workspaceFolder}\\c\\planarityApp\\planarityHelp.c", + "${workspaceFolder}\\c\\planarityApp\\planarityMenu.c", + "${workspaceFolder}\\c\\planarityApp\\planarityRandomGraphs.c", + "${workspaceFolder}\\c\\planarityApp\\planaritySpecificGraph.c", + "${workspaceFolder}\\c\\planarityApp\\planarityTestAllGraphs.c", + "${workspaceFolder}\\c\\planarityApp\\planarityTransformGraph.c", + "${workspaceFolder}\\c\\planarityApp\\planarityUtils.c", + "${workspaceFolder}\\c\\graphLib\\graphDFSUtils.c", + "${workspaceFolder}\\c\\graphLib\\graphUtils.c", + "${workspaceFolder}\\c\\graphLib\\extensionSystem\\graphExtensions.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK4Search.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK23Search.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK33Search.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK4Search_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK23Search_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK33Search_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\io\\g6-api-utilities.c", + "${workspaceFolder}\\c\\graphLib\\io\\g6-read-iterator.c", + "${workspaceFolder}\\c\\graphLib\\io\\g6-write-iterator.c", + "${workspaceFolder}\\c\\graphLib\\io\\graphIO.c", + "${workspaceFolder}\\c\\graphLib\\io\\strbuf.c", + "${workspaceFolder}\\c\\graphLib\\io\\strOrFile.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\apiutils.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\listcoll.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\stack.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphDrawPlanar.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphDrawPlanar_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphEmbed.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphIsolator.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphNonplanar.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphOuterplanarObstruction.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphTests.c", + "-o", + "${workspaceFolder}\\Debug\\planarity.exe" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: (Windows) [Release] gcc.exe build planarity project", + "command": "C:\\msys64\\ucrt64\\bin\\gcc.exe", + "args": [ + "-O3", + //"-DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "-Wall", + // "-Wextra", + // "-g", // Uncomment to generate debug symbols for Release build and debug with VSCode + "${workspaceFolder}\\c\\planarityApp\\planarity.c", + "${workspaceFolder}\\c\\planarityApp\\planarityCommandLine.c", + "${workspaceFolder}\\c\\planarityApp\\planarityHelp.c", + "${workspaceFolder}\\c\\planarityApp\\planarityMenu.c", + "${workspaceFolder}\\c\\planarityApp\\planarityRandomGraphs.c", + "${workspaceFolder}\\c\\planarityApp\\planaritySpecificGraph.c", + "${workspaceFolder}\\c\\planarityApp\\planarityTestAllGraphs.c", + "${workspaceFolder}\\c\\planarityApp\\planarityTransformGraph.c", + "${workspaceFolder}\\c\\planarityApp\\planarityUtils.c", + "${workspaceFolder}\\c\\graphLib\\graphDFSUtils.c", + "${workspaceFolder}\\c\\graphLib\\graphUtils.c", + "${workspaceFolder}\\c\\graphLib\\extensionSystem\\graphExtensions.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK4Search.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK23Search.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK33Search.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK4Search_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK23Search_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\graphK33Search_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\io\\g6-api-utilities.c", + "${workspaceFolder}\\c\\graphLib\\io\\g6-read-iterator.c", + "${workspaceFolder}\\c\\graphLib\\io\\g6-write-iterator.c", + "${workspaceFolder}\\c\\graphLib\\io\\graphIO.c", + "${workspaceFolder}\\c\\graphLib\\io\\strbuf.c", + "${workspaceFolder}\\c\\graphLib\\io\\strOrFile.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\apiutils.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\listcoll.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\stack.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphDrawPlanar.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphDrawPlanar_Extensions.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphEmbed.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphIsolator.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphNonplanar.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphOuterplanarObstruction.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\graphTests.c", + "-o", + "${workspaceFolder}\\Release\\planarity.exe" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: (Linux) [Debug] gcc build planarity project", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-DDEBUG", // Added so that _DEBUG is defined; see appconst.h + //"-DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "-Wall", + // "-Wextra", + "-g", // Generate debug symbols + "${workspaceFolder}/c/planarityApp/**.c", + "${workspaceFolder}/c/graphLib/**.c", + "${workspaceFolder}/c/graphLib/extensionSystem/**.c", + "${workspaceFolder}/c/graphLib/homeomorphSearch/**.c", + "${workspaceFolder}/c/graphLib/io/**.c", + "${workspaceFolder}/c/graphLib/lowLevelUtils/**.c", + "${workspaceFolder}/c/graphLib/planarityRelated/**.c", + "-o", + "${workspaceFolder}/Debug/planarity" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "compiler: /usr/bin/gcc" + }, + { + "type": "cppbuild", + "label": "C/C++: (Linux) [Release] gcc build planarity project", + "command": "/usr/bin/gcc", + "args": [ + "-O3", + //"-DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "-Wall", + // "-Wextra", + // "-g", // Uncomment to generate debug symbols for Release build and debug with VSCode + "${workspaceFolder}/c/planarityApp/**.c", + "${workspaceFolder}/c/graphLib/**.c", + "${workspaceFolder}/c/graphLib/extensionSystem/**.c", + "${workspaceFolder}/c/graphLib/homeomorphSearch/**.c", + "${workspaceFolder}/c/graphLib/io/**.c", + "${workspaceFolder}/c/graphLib/lowLevelUtils/**.c", + "${workspaceFolder}/c/graphLib/planarityRelated/**.c", + "-o", + "${workspaceFolder}/Release/planarity" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "compiler: /usr/bin/gcc" + }, + { + "type": "cppbuild", + "label": "C/C++: (Windows) [Debug] cl.exe build planarity project", + "command": "cl.exe", + "args": [ + "/Od", // Disables optimization, which speeds compilation and simplifies debugging + "/Zi", // Generate debug symbols + "/EHsc", + "/nologo", + "/DDEBUG", // Added so that _DEBUG is defined; see appconst.h + //"/DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "/Wall", + "/wd4464", // Ignore warnings about relative paths + "/wd4710", // Ignore warnings about not inlining scanf, sprintf, printf, fprintf, etc. + "/wd4711", // Ignore warnings about automatic inline expansion, as we are not concerned with code size + "/wd4774", // Ignore warnings about format string not being a string literal; requires developer caution! + "/wd4996", // Ignore safety warnings about sprintf, scanf, strcpy, strcat, fopen, etc. + "/wd5045", // Ignore Spectre mitigation warning + "/external:anglebrackets", + "/external:W0", // Ignore warnings thrown by external headers + "/Fe${workspaceFolder}\\Debug\\planarity.exe", + "${workspaceFolder}\\c\\planarityApp\\**.c", + "${workspaceFolder}\\c\\graphLib\\**.c", + "${workspaceFolder}\\c\\graphLib\\extensionSystem\\**.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\**.c", + "${workspaceFolder}\\c\\graphLib\\io\\**.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\**.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\**.c" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$msCompile" + ], + "group": { + "kind": "build" + }, + "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: (Windows) [Release] cl.exe build planarity project", + "command": "cl.exe", + "args": [ + "/O2", // This is the maximum optimization level available for MSVC cl + "/nologo", + //"/DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "/Wall", + "/wd4464", // Ignore warnings about relative paths + "/wd4710", // Ignore warnings about not inlining scanf, sprintf, printf, fprintf, etc. + "/wd4711", // Ignore warnings about automatic inline expansion, as we are not concerned with code size + "/wd4774", // Ignore warnings about format string not being a string literal; requires developer caution! + "/wd4996", // Ignore safety warnings about sprintf, scanf, strcpy, strcat, fopen, etc. + "/wd5045", // Ignore Spectre mitigation warning + "/external:anglebrackets", + "/external:W0", // Ignore warnings thrown by external headers + "/Fe${workspaceFolder}\\Release\\planarity.exe", + "${workspaceFolder}\\c\\planarityApp\\**.c", + "${workspaceFolder}\\c\\graphLib\\**.c", + "${workspaceFolder}\\c\\graphLib\\extensionSystem\\**.c", + "${workspaceFolder}\\c\\graphLib\\homeomorphSearch\\**.c", + "${workspaceFolder}\\c\\graphLib\\io\\**.c", + "${workspaceFolder}\\c\\graphLib\\lowLevelUtils\\**.c", + "${workspaceFolder}\\c\\graphLib\\planarityRelated\\**.c" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$msCompile" + ], + "group": { + "kind": "build" + }, + "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: (MacOS) [Debug] clang build planarity project", + "command": "clang", + "args": [ + "-fcolor-diagnostics", + "-fansi-escape-codes", + "-DDEBUG", // Added so that _DEBUG is defined; see appconst.h + //"-DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "-Wall", + // "-Wextra", + "-g", // Generate debug symbols + "${workspaceFolder}/c/planarityApp/**.c", + "${workspaceFolder}/c/graphLib/**.c", + "${workspaceFolder}/c/graphLib/extensionSystem/**.c", + "${workspaceFolder}/c/graphLib/homeomorphSearch/**.c", + "${workspaceFolder}/c/graphLib/io/**.c", + "${workspaceFolder}/c/graphLib/lowLevelUtils/**.c", + "${workspaceFolder}/c/graphLib/planarityRelated/**.c", + "-o", + "${workspaceFolder}/Debug/planarity" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: (MacOS) [Release] clang build planarity project", + "command": "clang", + "args": [ + "-O3", + //"-DUSE_0BASEDARRAYS", // Commented out to use faster 1-based array indexing + "-Wall", + // "-Wextra", + // "-g", // Uncomment to generate debug symbols for Release build and debug with VSCode + "${workspaceFolder}/c/planarityApp/**.c", + "${workspaceFolder}/c/graphLib/**.c", + "${workspaceFolder}/c/graphLib/extensionSystem/**.c", + "${workspaceFolder}/c/graphLib/homeomorphSearch/**.c", + "${workspaceFolder}/c/graphLib/io/**.c", + "${workspaceFolder}/c/graphLib/lowLevelUtils/**.c", + "${workspaceFolder}/c/graphLib/planarityRelated/**.c", + "-o", + "${workspaceFolder}/Release/planarity" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": "build", + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/.pylintrc b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/.pylintrc new file mode 100644 index 000000000..e4866c9ab --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/.pylintrc @@ -0,0 +1,645 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint +# in a server-like mode. +clear-cache-post-run=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, +# it can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked and +# will not be imported (useful for modules/projects where namespaces are +# manipulated during runtime and thus existing member attributes cannot be +# deduced by static analysis). It supports qualified module names, as well as +# Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Resolve imports to .pyi stubs if available. May reduce no-member messages and +# increase not-an-iterable messages. +; prefer-stubs=no ; pylint throws unrecognized command error??? + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.11 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type alias names. If left empty, type +# alias names will be checked with the set naming style. +#typealias-rgx= + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + asyncSetUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=builtins.BaseException,builtins.Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow explicit reexports by alias from a package __init__. +allow-reexport-from-package=no + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + use-implicit-booleaness-not-comparison-to-string, + use-implicit-booleaness-not-comparison-to-zero, + invalid-name + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + +# Let 'consider-using-join' be raised when the separator to join on would be +# non-empty (resulting in expected fixes of the type: ``"- " + " - +# ".join(items)``) +; suggest-join-with-non-empty-separator=yes ; pylint throws unrecognized command error??? + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are: text, parseable, colorized, +# json2 (improved json format), json (old json format) and msvs (visual +# studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. No available dictionaries : You need to install +# both the python package and the system dependency for enchant to work. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config.ini b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config.ini new file mode 100644 index 000000000..1ce08ff90 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config.ini @@ -0,0 +1,35 @@ +[RandomGraphs] +enabled = False +perform_full_analysis = True +num_graphs = 500 +order = 1000 +commands_to_run = + +[RandomMaxPlanarGraphGenerator] +enabled = False +perform_full_analysis = True +order = 1000 + +[SpecificGraph] +enabled = False +perform_full_analysis = True +infile_path = >>CHANGEME<< +commands_to_run = + +[RandomNonplanarGraphGenerator] +enabled = False +perform_full_analysis = True +order = 1000 + +[TransformGraph] +enabled = False +perform_full_analysis = True +infile_path = >>CHANGEME<< +output_formats_to_test = + +[TestAllGraphs] +enabled = False +perform_full_analysis = True +infile_path = >>CHANGEME<< +commands_to_run = + diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/devEnvSetup.sh b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/devEnvSetup.sh new file mode 100755 index 000000000..4998f9e0d --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/devEnvSetupAndDefaults/devEnvSetup.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +echo -e "Copying VSCode configuration files.\n" +cp -R .vscode ../ + +echo -e "Copying Python TestSupport pylint configuration file.\n" +cp TestSupport/planaritytesting/.pylintrc ../TestSupport/planaritytesting/ + +echo -e "Copying planarity leaks orchestrator default configuration file.\n" +cp TestSupport/planaritytesting/leaksorchestrator/planarity_leaks_config.ini ../TestSupport/planaritytesting/leaksorchestrator/ + +echo -e "Making directories for Random Graphs output.\n" +mkdir -p ../random +mkdir -p ../embedded +mkdir -p ../adjlist +mkdir -p ../obstructed +mkdir -p ../error + +echo -e "Making m4 directory for autotools build process.\n" +mkdir -p ../m4 diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/libplanarity.pc.in b/extern/edge-addition-planarity-suite-Version_4.0.0.0/libplanarity.pc.in new file mode 100644 index 000000000..31c31ebd8 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/libplanarity.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +URL: https://github.com/graph-algorithms/edge-addition-planarity-suite +Description: Edge Addition Planarity Suite Graph Library +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lplanarity +Cflags: -I${includedir} diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/test-samples.sh.in b/extern/edge-addition-planarity-suite-Version_4.0.0.0/test-samples.sh.in new file mode 100755 index 000000000..1c899d266 --- /dev/null +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/test-samples.sh.in @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Run "planarity -test" using planarity from the build tree and the +# samples from the source tree. +# + +samplesdir="@abs_top_srcdir@/c/samples" +planaritydir="@abs_top_builddir@" + +cd "${planaritydir}" || exit 1 +./planarity -test "${samplesdir}" || exit 2 From 553676f5bb72ec639632c29b8bca3654113333ac Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 11:50:38 +0200 Subject: [PATCH 3/7] Remove old files --- .../.gitignore | 65 - .../.project | 11 - .../Makefile.am | 56 - .../README.md | 89 - .../autogen.sh | 7 - .../c/.cproject | 716 ----- .../c/.gdbinit | 1 - .../c/.project | 69 - .../c/appconst.h | 92 - .../c/graph.h | 129 - .../c/graphDFSUtils.c | 468 --- .../c/graphDrawPlanar.c | 1056 ------- .../c/graphDrawPlanar.h | 28 - .../c/graphDrawPlanar.private.h | 91 - .../c/graphDrawPlanar_Extensions.c | 696 ----- .../c/graphEmbed.c | 1378 --------- .../c/graphExtensions.c | 559 ---- .../c/graphExtensions.h | 36 - .../c/graphExtensions.private.h | 34 - .../c/graphFunctionTable.h | 81 - .../c/graphIO.c | 774 ----- .../c/graphIsolator.c | 840 ------ .../c/graphK23Search.c | 280 -- .../c/graphK23Search.h | 26 - .../c/graphK23Search.private.h | 28 - .../c/graphK23Search_Extensions.c | 264 -- .../c/graphK33Search.c | 2064 -------------- .../c/graphK33Search.h | 26 - .../c/graphK33Search.private.h | 63 - .../c/graphK33Search_Extensions.c | 762 ----- .../c/graphK4Search.c | 1490 ---------- .../c/graphK4Search.h | 26 - .../c/graphK4Search.private.h | 60 - .../c/graphK4Search_Extensions.c | 511 ---- .../c/graphNonplanar.c | 724 ----- .../c/graphOuterplanarObstruction.c | 212 -- .../c/graphStructures.h | 794 ------ .../c/graphTests.c | 976 ------- .../c/graphUtils.c | 2511 ----------------- .../c/listcoll.c | 272 -- .../c/listcoll.h | 121 - .../c/planarity.c | 242 -- .../c/planarity.h | 85 - .../c/planarityCommandLine.c | 396 --- .../c/planarityRandomGraphs.c | 500 ---- .../c/planaritySpecificGraph.c | 194 -- .../c/planarityUtils.c | 427 --- .../c/platformTime.h | 53 - .../c/stack.c | 223 -- .../c/stack.h | 97 - .../configure.ac | 31 - .../planarity.1 | 92 - 52 files changed, 20826 deletions(-) delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/.gitignore delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/.project delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/Makefile.am delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/README.md delete mode 100755 extern/edge-addition-planarity-suite-Version_3.0.1.0/autogen.sh delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.cproject delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.gdbinit delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.project delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/appconst.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graph.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDFSUtils.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.private.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar_Extensions.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphEmbed.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.private.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphFunctionTable.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIO.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIsolator.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.private.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search_Extensions.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.private.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search_Extensions.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.private.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search_Extensions.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphNonplanar.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphOuterplanarObstruction.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphStructures.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphTests.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphUtils.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityCommandLine.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityRandomGraphs.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planaritySpecificGraph.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityUtils.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/platformTime.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.c delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.h delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/configure.ac delete mode 100644 extern/edge-addition-planarity-suite-Version_3.0.1.0/planarity.1 diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/.gitignore b/extern/edge-addition-planarity-suite-Version_3.0.1.0/.gitignore deleted file mode 100644 index 47a190c69..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/.gitignore +++ /dev/null @@ -1,65 +0,0 @@ -# Directories -/c/Release/ -/c/Debug/ -/c/.settings/ -/c/error/ - -/c/random/ -/c/embedded/ -/c/adjlist/ -/c/obstructed/ - -/c/Release/random/ -/c/Release/embedded/ -/c/Release/adjlist/ -/c/Release/obstructed/ -/c/Release/error/ - -/c/Debug/random/ -/c/Debug/embedded/ -/c/Debug/adjlist/ -/c/Debug/obstructed/ -/c/Debug/error/ - -# Object files -*.o -*.ko -*.obj -*.elf - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ - -# Java -*.class -*.log -*.jar -*.war -*.ear -*.zip -*.tar.gz -*.rar diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/.project b/extern/edge-addition-planarity-suite-Version_3.0.1.0/.project deleted file mode 100644 index af6fc1605..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/.project +++ /dev/null @@ -1,11 +0,0 @@ - - - Planarity-trunk - - - - - - - - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/Makefile.am b/extern/edge-addition-planarity-suite-Version_3.0.1.0/Makefile.am deleted file mode 100644 index 45a475857..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/Makefile.am +++ /dev/null @@ -1,56 +0,0 @@ -ACLOCAL_AMFLAGS = -I m4 - -lib_LTLIBRARIES = libplanarity.la - -libplanarity_la_SOURCES = \ - c/graphDFSUtils.c \ - c/graphDrawPlanar.c \ - c/graphDrawPlanar_Extensions.c \ - c/graphEmbed.c \ - c/graphExtensions.c \ - c/graphIO.c \ - c/graphIsolator.c \ - c/graphK23Search.c \ - c/graphK23Search_Extensions.c \ - c/graphK33Search.c \ - c/graphK33Search_Extensions.c \ - c/graphK4Search.c \ - c/graphK4Search_Extensions.c \ - c/graphNonplanar.c \ - c/graphOuterplanarObstruction.c \ - c/graphTests.c \ - c/graphUtils.c \ - c/listcoll.c \ - c/planarityRandomGraphs.c \ - c/planaritySpecificGraph.c \ - c/planarityUtils.c \ - c/stack.c -libplanarity_la_LDFLAGS = -no-undefined $(AM_LDFLAGS) -version-info @LT_CURRENT@:@LT_REVISION@:@LT_AGE@ - -headersdir = $(includedir)/planarity/ -headers_HEADERS = \ - c/appconst.h \ - c/graph.h \ - c/graphDrawPlanar.h \ - c/graphDrawPlanar.private.h \ - c/graphExtensions.h \ - c/graphExtensions.private.h \ - c/graphFunctionTable.h \ - c/graphK23Search.h \ - c/graphK23Search.private.h \ - c/graphK33Search.h \ - c/graphK33Search.private.h \ - c/graphK4Search.h \ - c/graphK4Search.private.h \ - c/graphStructures.h \ - c/listcoll.h \ - c/planarity.h \ - c/platformTime.h \ - c/stack.h - -bin_PROGRAMS = planarity -planarity_LDADD = libplanarity.la -planarity_SOURCES = c/planarity.c c/planarityCommandLine.c - -man1_MANS = planarity.1 -EXTRA_DIST=$(man1_MANS) diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/README.md b/extern/edge-addition-planarity-suite-Version_3.0.1.0/README.md deleted file mode 100644 index f31760bf4..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# Edge Addition Planarity Suite - -The primary purpose of this repository is to provide implementations of the edge addition planar graph embedding algorithm and related algorithms, including a planar graph drawing method, an isolator for a minimal subgraph obstructing planarity in non-planar graphs, outerplanar graph embedder and obstruction isolator algorithms, and tester/isolator algorithms for subgraphs homeomorphic to _K2,3_, _K4_, and _K3,3_. The C implementations in this repository are the reference implementations of algorithms appearing in the following papers: - -* [Subgraph Homeomorphism via the Edge Addition Planarity Algorithm](http://dx.doi.org/10.7155/jgaa.00268) - -* [A New Method for Efficiently Generating Planar Graph Visibility Representations](http://dx.doi.org/10.1007/11618058_47) - -* [On the Cutting Edge: Simplified O(n) Planarity by Edge Addition](http://dx.doi.org/10.7155/jgaa.00091) - -* [Simplified O(n) Algorithms for Planar Graph Embedding, Kuratowski Subgraph Isolation, and Related Problems](https://dspace.library.uvic.ca/handle/1828/9918) - -As secondary purpose of this repository is to provide a generalized graph API that enables implementation of a very wide range of in-memory graph algorithms including basic methods for reading, writing, depth first search, and lowpoint as well as advanced methods for solving planarity, outerplanarity, drawing, and selected subgraph homeomorphism problems. An extension mechanism is also provided to enable implementation of planarity-related algorithms by overriding and augmenting data structures and methods of the core planarity algorithm. - -## Getting Started - -These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. - -### Pre-compiled Executable Releases for Non-Developers - -This repository includes releases of already compiled versions of an application for non-developers to use the algorithm implementations. Download and decompress planarity._Platform_.zip. If you execute the _planarity.exe_ program with a "-test" parameter, the algorithms will be performed on the graphs in the files in the _samples_ directory, and a success result will be reported. - -The application includes a menu driven mode that lets a user manually select algorithms to run and, where appropriate, files containing graphs on which to run the algorithms. To access the menu mode, execute _planarity.exe_ without any command line parameters. - -The application also supports an extensive list of command-line parameters that make it possible to automate the execution of any of the algorithms included in the application. Execute _planarity.exe_ with the "-h" command-line parameter to get more information about the command line options, and use "-h -menu" for more extensive information about command-line mode. - -### Setting up a Development Environment - -A development environment for the C reference implementations can be set up based on Eclipse. - -1. Install a recent version of the Java JDK (such as Java version 14 or higher) -2. Ensure that you set the JAVA_HOME system environment variable (e.g. to c:\Program Files\Java\jdk-14.0.1) -3. Ensure that you add %JAVA_HOME%\bin to your system PATH -4. Install Eclipse, such as the "Eclipse IDE for Enterprise Java Developers" -5. Install gcc, gdb, and msys (e.g. download and run mingw-get-setup.exe from [here](https://osdn.net/projects/mingw/releases/) and then use the package installer to install C and C++, GDB, MSYS, and any other packages you may want.) -6. Ensure your gcc is accessible from the command line (e.g. add C:\MinGW\bin to the system PATH) -7. In Eclipse, and install the C Development Tools (CDT) - 1. In Eclipse, choose the menu option Help > Install New Software - 2. Choose to work with the main repository (e.g. 2020 - 06 - http://download.eclipse.org/releases) - 3. Under Programming Languages, choose C/C++ Autotools, C/C++ Development Tools, C/C++ Development Tools SDK, C/C++ Library API Documentation Hover Help, and C/C++ Unit Testing Support - -### Working with the Code in the Development Environment - -In this repository, the "Code" button provides the [HTTPS clone](https://github.com/graph-algorithms/edge-addition-planarity-suite.git) link to use to get the code. - -1. In Eclipse, use the Open Perspectives button (or Window | Perspective | Open Perspective | Git) -2. In the web browser, go to the "Code" button, HTTPS clone, and get the URL into the copy/paste clipboard. -3. Go to the Eclipse Git Repositories panel, and clic "Clone a Git repository" -4. The URI, Host, and Repository are pre-filled correctly from the copy/paste clipboard. -5. Leave the User/Password blank, and hit Next -6. The master branch is selected by default, so just hit Next again -7. Change the destination directory to a subdirectory where you want to store the project code (e.g. c:\Users\_you_\Documents\eclipse\workspaces-cpp\graph-algorithms\edge-addition-planarity-suite) -8. Hit Finish - -Once you have the code, you will be able to import the project, build the _planarity.exe_ application and use the run and debug features of Eclipse. - -1. Use the Open Perspectives button (or Windows | Perspective | Open Perspective | Other…) -2. Select C/C++ -3. In the Project Explorer window, click Import projects... -4. Choose General | Existing Projects into Workspace and hit Next > -5. For "Select root directory:" choose "Browse..." -6. Navigate to .../edge-addition-planarity-suite/c (This autofills and selects Planarity-C in Projects list) -7. Hit Finish (do not select "Copy projects into workspace") -8. Right-click Planarity-C project, Build Configurations, Build All -9. Right-click Planarity-C project, Build Configurations, Set Active, Release -10. Right-click Planarity-C project, Run As, Local Application, planarity.exe (release) - -## Contributing - -Subject to your acceptance of the license agreement, contributions can be made via a pull request. Before submitting a pull request, please ensure that you have set your github user name and email within your development environment. For Eclipse, you can use the following steps: - -1. Window > Preferences > Team > Git > Configuration -2. Add Entry... user.name (set the value to your github identity) -3. Add Entry... user.email (set the value to the primary email of your github identity) -4. Hit Apply and Close - -## Versioning - -The APIs for the graph library and the planarity algorithm implementations are versioned using the method documented in [configure.ac](configure.ac). - -The _planarity.exe_ application, which provides command-line and menu-driven interfaces for the graph library and planarity algorithms, is versioned according to the _Major.Minor.Maintenance.Tweak_ numbering system documented in the comments in [planarity.c](c/planarity.c). - -## License - -This project is licensed under a 3-clause BSD License appearing in [LICENSE.TXT](LICENSE.TXT). - -## Related Works and Further Documentation - -There have been successful technology transfers of the implementation code and/or algorithms of this project into other projects. To see a list of the related projects and for further documentation about this project, please see the [project wiki](https://github.com/graph-algorithms/edge-addition-planarity-suite/wiki). diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/autogen.sh b/extern/edge-addition-planarity-suite-Version_3.0.1.0/autogen.sh deleted file mode 100755 index 647a17e4f..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/autogen.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -aclocal && -autoconf && -libtoolize --copy && -automake --add-missing --copy && -rm -rf autom4te.cache diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.cproject b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.cproject deleted file mode 100644 index 4007aa3a2..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.cproject +++ /dev/null @@ -1,716 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.gdbinit b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.gdbinit deleted file mode 100644 index f84981e19..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.gdbinit +++ /dev/null @@ -1 +0,0 @@ -set new-console on diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.project b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.project deleted file mode 100644 index 3c8054fee..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/.project +++ /dev/null @@ -1,69 +0,0 @@ - - - Planarity-C - - - - - - org.eclipse.cdt.managedbuilder.core.genmakebuilder - clean,full,incremental, - - - ?name? - - - - org.eclipse.cdt.make.core.append_environment - true - - - org.eclipse.cdt.make.core.buildArguments - - - - org.eclipse.cdt.make.core.buildCommand - make - - - org.eclipse.cdt.make.core.buildLocation - ${workspace_loc:/Planarity-C/Debug} - - - org.eclipse.cdt.make.core.contents - org.eclipse.cdt.make.core.activeConfigSettings - - - org.eclipse.cdt.make.core.enableAutoBuild - false - - - org.eclipse.cdt.make.core.enableCleanBuild - true - - - org.eclipse.cdt.make.core.enableFullBuild - true - - - org.eclipse.cdt.make.core.stopOnError - true - - - org.eclipse.cdt.make.core.useDefaultBuildCmd - true - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder - - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - org.eclipse.cdt.managedbuilder.core.managedBuildNature - org.eclipse.cdt.core.cnature - - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/appconst.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/appconst.h deleted file mode 100644 index 4e44ee9b7..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/appconst.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef APPCONST_H -#define APPCONST_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifdef WIN32 -#define WINDOWS -#endif - -/* Defines fopen strings for reading and writing text files on PC and UNIX */ - -#ifdef WINDOWS -#define READTEXT "rt" -#define WRITETEXT "wt" -#define FILE_DELIMITER '\\' -#else -#define READTEXT "r" -#define WRITETEXT "w" -#define FILE_DELIMITER '/' -#endif - -// When PROFILE is defined, prints out run-time stats on a number of subordinate -// routines in the embedder - -//#define PROFILE -#ifdef PROFILE -#include "platformTime.h" -#endif - -/* Define DEBUG to get additional debugging. The default is to define it when MSC does */ - -#ifdef _DEBUG -#define DEBUG -#endif - -/* Some low-level functions are replaced by faster macros, except when debugging */ - -#define SPEED_MACROS -#ifdef DEBUG -#undef SPEED_MACROS -#endif - -/* Return status values; OK/NOTOK behave like Boolean true/false, - not like program exit codes. */ - -#define OK 1 -#define NOTOK 0 - -#ifdef DEBUG -#undef NOTOK -extern int debugNOTOK(); -#include -#define NOTOK (printf("NOTOK on Line %d of %s\n", __LINE__, __FILE__), debugNOTOK()) -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef NULL -#define NULL 0L -#endif - -/* Array indices are used as pointers, and NIL means bad pointer */ - -// This definition is used with 1-based array indexing -#define NIL 0 -#define NIL_CHAR 0x00 - -// This definition is used in combination with 0-based array indexing -//#define NIL -1 -//#define NIL_CHAR 0xFF - -/******************************************************************** - A few simple integer selection macros - ********************************************************************/ - -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) - -#define MIN3(x, y, z) MIN(MIN((x), (y)), MIN((y), (z))) -#define MAX3(x, y, z) MAX(MAX((x), (y)), MAX((y), (z))) - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graph.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graph.h deleted file mode 100644 index d765c6874..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graph.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef GRAPH_H -#define GRAPH_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "graphStructures.h" - -#include "graphExtensions.h" - -/////////////////////////////////////////////////////////////////////////////// -// Definitions for higher-order operations at the vertex, edge and graph levels -/////////////////////////////////////////////////////////////////////////////// - -graphP gp_New(void); - -int gp_InitGraph(graphP theGraph, int N); -void gp_ReinitializeGraph(graphP theGraph); -int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph); -int gp_CopyGraph(graphP dstGraph, graphP srcGraph); -graphP gp_DupGraph(graphP theGraph); - -int gp_CreateRandomGraph(graphP theGraph); -int gp_CreateRandomGraphEx(graphP theGraph, int numEdges); - -void gp_Free(graphP *pGraph); - -int gp_Read(graphP theGraph, char *FileName); -#define WRITE_ADJLIST 1 -#define WRITE_ADJMATRIX 2 -#define WRITE_DEBUGINFO 3 -int gp_Write(graphP theGraph, char *FileName, int Mode); - -int gp_IsNeighbor(graphP theGraph, int u, int v); -int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v); -int gp_GetVertexDegree(graphP theGraph, int v); -int gp_GetVertexInDegree(graphP theGraph, int v); -int gp_GetVertexOutDegree(graphP theGraph, int v); - -int gp_GetArcCapacity(graphP theGraph); -int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink); -int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink, - int v, int e_v, int e_vlink); - -void gp_HideEdge(graphP theGraph, int e); -void gp_RestoreEdge(graphP theGraph, int e); -int gp_HideVertex(graphP theGraph, int vertex); -int gp_DeleteEdge(graphP theGraph, int e, int nextLink); - -int gp_ContractEdge(graphP theGraph, int e); -int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore); -int gp_RestoreVertices(graphP theGraph); - -int gp_CreateDFSTree(graphP theGraph); -int gp_SortVertices(graphP theGraph); -int gp_LowpointAndLeastAncestor(graphP theGraph); -int gp_PreprocessForEmbedding(graphP theGraph); - -int gp_Embed(graphP theGraph, int embedFlags); -int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult); - -/* Possible Flags for gp_Embed. The planar and outerplanar settings are supported - natively. The rest require extension modules. */ - -#define EMBEDFLAGS_PLANAR 1 -#define EMBEDFLAGS_OUTERPLANAR 2 - -#define EMBEDFLAGS_DRAWPLANAR (4|EMBEDFLAGS_PLANAR) - -#define EMBEDFLAGS_SEARCHFORK23 (16|EMBEDFLAGS_OUTERPLANAR) -#define EMBEDFLAGS_SEARCHFORK4 (32|EMBEDFLAGS_OUTERPLANAR) -#define EMBEDFLAGS_SEARCHFORK33 (64|EMBEDFLAGS_PLANAR) - -#define EMBEDFLAGS_SEARCHFORK5 (128|EMBEDFLAGS_PLANAR) - -#define EMBEDFLAGS_MAXIMALPLANARSUBGRAPH 256 -#define EMBEDFLAGS_PROJECTIVEPLANAR 512 -#define EMBEDFLAGS_TOROIDAL 1024 - -/* If LOGGING is defined, then write to the log, otherwise no-op - By default, neither release nor DEBUG builds including LOGGING. - Logging is useful for seeing details of how various algorithms - handle a particular graph. */ - -//#define LOGGING -#ifdef LOGGING - -#define gp_LogLine _LogLine -#define gp_Log _Log - -void _LogLine(char *Line); -void _Log(char *Line); - -#define gp_MakeLogStr1 _MakeLogStr1 -#define gp_MakeLogStr2 _MakeLogStr2 -#define gp_MakeLogStr3 _MakeLogStr3 -#define gp_MakeLogStr4 _MakeLogStr4 -#define gp_MakeLogStr5 _MakeLogStr5 - -char *_MakeLogStr1(char *format, int); -char *_MakeLogStr2(char *format, int, int); -char *_MakeLogStr3(char *format, int, int, int); -char *_MakeLogStr4(char *format, int, int, int, int); -char *_MakeLogStr5(char *format, int, int, int, int, int); - -#else -#define gp_LogLine(Line) -#define gp_Log(Line) -#define gp_MakeLogStr1(format, one) -#define gp_MakeLogStr2(format, one, two) -#define gp_MakeLogStr3(format, one, two, three) -#define gp_MakeLogStr4(format, one, two, three, four) -#define gp_MakeLogStr5(format, one, two, three, four, five) -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDFSUtils.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDFSUtils.c deleted file mode 100644 index 91e45d90d..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDFSUtils.c +++ /dev/null @@ -1,468 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#define GRAPHDFSUTILS_C - -#include "graph.h" - -extern void _ClearVertexVisitedFlags(graphP theGraph, int); - -/******************************************************************** - gp_CreateDFSTree - Assigns Depth First Index (DFI) to each vertex. Also records parent - of each vertex in the DFS tree, and marks DFS tree edges that go from - parent to child. Forward arc cycle edges are also distinguished from - edges leading from a DFS tree descendant to an ancestor-- both DFS tree - edges and back arcs. The forward arcs are moved to the end of the - adjacency list to make the set easier to find and process. - - NOTE: This is a utility function provided for general use of the graph - library. The core planarity algorithm uses its own DFS in order to build - up related data structures at the same time as the DFS tree is created. - ********************************************************************/ - -#include "platformTime.h" - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -int gp_CreateDFSTree(graphP theGraph) -{ -stackP theStack; -int DFI, v, uparent, u, e; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - if (theGraph==NULL) return NOTOK; - if (theGraph->internalFlags & FLAGS_DFSNUMBERED) return OK; - - gp_LogLine("\ngraphDFSUtils.c/gp_CreateDFSTree() start"); - - theStack = theGraph->theStack; - -/* There are 2M edge records (arcs) and for each we can push 2 integers, - so a stack of 2 * arcCapacity integers suffices. - This is already in theGraph structure, so we make sure it's empty, - then clear all visited flags in prep for the Depth first search. */ - - if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph)) - return NOTOK; - - sp_ClearStack(theStack); - - _ClearVertexVisitedFlags(theGraph, FALSE); - -/* This outer loop causes the connected subgraphs of a disconnected - graph to be numbered */ - - for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++) - { - if (gp_IsNotDFSTreeRoot(theGraph, v)) - continue; - - sp_Push2(theStack, NIL, NIL); - while (sp_NonEmpty(theStack)) - { - sp_Pop2(theStack, uparent, e); - u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e); - - if (!gp_GetVertexVisited(theGraph, u)) - { - gp_LogLine(gp_MakeLogStr3("V=%d, DFI=%d, Parent=%d", u, DFI, uparent)); - - gp_SetVertexVisited(theGraph, u); - gp_SetVertexIndex(theGraph, u, DFI++); - gp_SetVertexParent(theGraph, u, uparent); - if (gp_IsArc(e)) - { - gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT); - } - - /* Push edges to all unvisited neighbors. These will be either - tree edges to children or forward arcs of back edges */ - - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - sp_Push2(theStack, u, e); - e = gp_GetNextArc(theGraph, e); - } - } - else - { - // If the edge leads to a visited vertex, then it is - // the forward arc of a back edge. - gp_SetEdgeType(theGraph, e, EDGE_TYPE_FORWARD); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_BACK); - } - } - } - - gp_LogLine("graphDFSUtils.c/gp_CreateDFSTree() end\n"); - - theGraph->internalFlags |= FLAGS_DFSNUMBERED; - -#ifdef PROFILE -platform_GetTime(end); -printf("DFS in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - gp_SortVertices() - Once depth first numbering has been applied to the graph, the index - member of each vertex contains the DFI. This routine can reorder the - vertices in linear time so that they appear in ascending order by DFI. - Note that the index field is then used to store the original number - of the vertex. Therefore, a second call to this method will put the - vertices back to the original order and put the DFIs back into the - index fields of the vertices. - - NOTE: This function is used by the core planarity algorithm, once its - custom DFS has assigned DFIs to the vertices. Once gp_Embed() has - finished creating an embedding or obstructing subgraph, this function - can be called to restore the original vertex numbering, if needed. - ********************************************************************/ - -int gp_SortVertices(graphP theGraph) -{ - return theGraph->functions.fpSortVertices(theGraph); -} - -int _SortVertices(graphP theGraph) -{ -int v, EsizeOccupied, e, srcPos, dstPos; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - if (theGraph == NULL) return NOTOK; - if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED)) - if (gp_CreateDFSTree(theGraph) != OK) - return NOTOK; - - gp_LogLine("\ngraphDFSUtils.c/_SortVertices() start"); - - /* Change labels of edges from v to DFI(v)-- or vice versa - Also, if any links go back to locations 0 to n-1, then they - need to be changed because we are reordering the vertices */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2) - { - if (gp_EdgeInUse(theGraph, e)) - { - gp_SetNeighbor(theGraph, e, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e))); - gp_SetNeighbor(theGraph, e+1, gp_GetVertexIndex(theGraph, gp_GetNeighbor(theGraph, e+1))); - } - } - - /* Convert DFSParent from v to DFI(v) or vice versa */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - if (gp_IsNotDFSTreeRoot(theGraph, v)) - gp_SetVertexParent(theGraph, v, gp_GetVertexIndex(theGraph, gp_GetVertexParent(theGraph, v))); - - /* Sort by 'v using constant time random access. Move each vertex to its - destination 'v', and store its source location in 'v'. */ - - /* First we clear the visitation flags. We need these to help mark - visited vertices because we change the 'v' field to be the source - location, so we cannot use index==v as a test for whether the - correct vertex is in location 'index'. */ - - _ClearVertexVisitedFlags(theGraph, FALSE); - - /* We visit each vertex location, skipping those marked as visited since - we've already moved the correct vertex into that location. The - inner loop swaps the vertex at location v into the correct position, - given by the index of the vertex at location v. Then it marks that - location as visited, then sets its index to be the location from - whence we obtained the vertex record. */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - srcPos = v; - while (!gp_GetVertexVisited(theGraph, v)) - { - dstPos = gp_GetVertexIndex(theGraph, v); - - gp_SwapVertexRec(theGraph, dstPos, theGraph, v); - gp_SwapVertexInfo(theGraph, dstPos, theGraph, v); - - gp_SetVertexVisited(theGraph, dstPos); - gp_SetVertexIndex(theGraph, dstPos, srcPos); - - srcPos = dstPos; - } - } - - /* Invert the bit that records the sort order of the graph */ - - theGraph->internalFlags ^= FLAGS_SORTEDBYDFI; - - gp_LogLine("graphDFSUtils.c/_SortVertices() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("SortVertices in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - gp_LowpointAndLeastAncestor() - leastAncestor(v): min(v, ancestor neighbors of v, excluding parent) - Lowpoint(v): min(leastAncestor(v), Lowpoint of DFS children of v) - - Lowpoint is computed via a post-order traversal of the DFS tree. - We push the root of the DFS tree, then we loop while the stack is not empty. - We pop a vertex; if it is not marked, then we are on our way down the DFS - tree, so we mark it and push it back on, followed by pushing its - DFS children. The next time we pop the node, all of its children - will have been popped, marked+children pushed, and popped again. On - the second pop of the vertex, we can therefore compute the lowpoint - values based on the childrens' lowpoints and the least ancestor from - among the edges in the vertex's adjacency list. - - If they have not already been performed, gp_CreateDFSTree() and - gp_SortVertices() are invoked on the graph, and it is left in the - sorted state on completion of this method. - - NOTE: This is a utility function provided for general use of the graph - library. The core planarity algorithm computes leastAncestor during its - initial DFS, and it computes the lowpoint of a vertex as it embeds the - tree edges to its children. - ********************************************************************/ - -int gp_LowpointAndLeastAncestor(graphP theGraph) -{ -stackP theStack = theGraph->theStack; -int v, u, uneighbor, e, L, leastAncestor; - - if (theGraph == NULL) return NOTOK; - - if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED)) - if (gp_CreateDFSTree(theGraph) != OK) - return NOTOK; - - if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI)) - if (gp_SortVertices(theGraph) != OK) - return NOTOK; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - gp_LogLine("\ngraphDFSUtils.c/gp_LowpointAndLeastAncestor() start"); - - // A stack of size N suffices because at maximum every vertex is pushed only once - // However, since a larger stack is needed for the main DFS, this is mainly documentation - if (sp_GetCapacity(theStack) < theGraph->N) - return NOTOK; - - sp_ClearStack(theStack); - - _ClearVertexVisitedFlags(theGraph, FALSE); - - // This outer loop causes the connected subgraphs of a disconnected graph to be processed - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);) - { - if (gp_GetVertexVisited(theGraph, v)) - { - ++v; - continue; - } - - sp_Push(theStack, v); - while (sp_NonEmpty(theStack)) - { - sp_Pop(theStack, u); - - // If not visited, then we're on the pre-order visitation, so push u and its DFS children - if (!gp_GetVertexVisited(theGraph, u)) - { - // Mark u as visited, then push it back on the stack - gp_SetVertexVisited(theGraph, u); - ++v; - sp_Push(theStack, u); - - // Push the DFS children of u - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push(theStack, gp_GetNeighbor(theGraph, e)); - } - - e = gp_GetNextArc(theGraph, e); - } - } - - // If u has been visited before, then this is the post-order visitation - else - { - // Start with high values because we are doing a min function - leastAncestor = L = u; - - // Compute leastAncestor and L, the least lowpoint from the DFS children - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - uneighbor = gp_GetNeighbor(theGraph, e); - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - if (L > gp_GetVertexLowpoint(theGraph, uneighbor)) - L = gp_GetVertexLowpoint(theGraph, uneighbor); - } - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) - { - if (leastAncestor > uneighbor) - leastAncestor = uneighbor; - } - - e = gp_GetNextArc(theGraph, e); - } - - /* Assign leastAncestor and Lowpoint to the vertex */ - gp_SetVertexLeastAncestor(theGraph, u, leastAncestor); - gp_SetVertexLowpoint(theGraph, u, leastAncestor < L ? leastAncestor : L); - } - } - } - - gp_LogLine("graphDFSUtils.c/gp_LowpointAndLeastAncestor() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("Lowpoint in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - gp_LeastAncestor() - - By simple pre-order visitation, compute the least ancestor of each - vertex that is directly adjacent to the vertex by a back edge. - - If they have not already been performed, gp_CreateDFSTree() and - gp_SortVertices() are invoked on the graph, and it is left in the - sorted state on completion of this method. - - NOTE: This method is not called by gp_LowpointAndLeastAncestor(), - which computes both values at the same time. - - NOTE: This method is useful in core planarity initialization when - a graph has already been DFS numbered and sorted by DFI. For example, - this allows the core planarity embedder to avoid perturbing unit test - graphs that may be designed and stored in a DFI sorted format. - ********************************************************************/ - -int gp_LeastAncestor(graphP theGraph) -{ -stackP theStack = theGraph->theStack; -int v, u, uneighbor, e, leastAncestor; - - if (theGraph == NULL) return NOTOK; - - if (!(theGraph->internalFlags&FLAGS_DFSNUMBERED)) - if (gp_CreateDFSTree(theGraph) != OK) - return NOTOK; - - if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI)) - if (gp_SortVertices(theGraph) != OK) - return NOTOK; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - gp_LogLine("\ngraphDFSUtils.c/gp_LeastAncestor() start"); - - // A stack of size N suffices because at maximum every vertex is pushed only once - if (sp_GetCapacity(theStack) < theGraph->N) - return NOTOK; - - sp_ClearStack(theStack); - - // This outer loop causes the connected subgraphs of a disconnected graph to be processed - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v);) - { - if (gp_GetVertexVisited(theGraph, v)) - { - ++v; - continue; - } - - sp_Push(theStack, v); - while (sp_NonEmpty(theStack)) - { - sp_Pop(theStack, u); - - if (!gp_GetVertexVisited(theGraph, u)) - { - gp_SetVertexVisited(theGraph, u); - ++v; - leastAncestor = u; - - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - uneighbor = gp_GetNeighbor(theGraph, e); - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push(theStack, uneighbor); - } - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) - { - if (leastAncestor > uneighbor) - leastAncestor = uneighbor; - } - - e = gp_GetNextArc(theGraph, e); - } - gp_SetVertexLeastAncestor(theGraph, u, leastAncestor); - } - } - } - - gp_LogLine("graphDFSUtils.c/gp_LeastAncestor() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("LeastAncestor in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.c deleted file mode 100644 index a63df776e..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.c +++ /dev/null @@ -1,1056 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -#include "graphDrawPlanar.h" -#include "graphDrawPlanar.private.h" - -extern int DRAWPLANAR_ID; - -#include "graph.h" - -#include -#include -#include - -extern void _ClearVisitedFlags(graphP theGraph); - -/* Private functions exported to system */ - -void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink); -int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink); - -int _ComputeVisibilityRepresentation(DrawPlanarContext *context); -int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context); - -/* Private functions */ -int _ComputeVertexPositions(DrawPlanarContext *context); -int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pIndex); -int _ComputeEdgePositions(DrawPlanarContext *context); -int _ComputeVertexRanges(DrawPlanarContext *context); -int _ComputeEdgeRanges(DrawPlanarContext *context); - -/******************************************************************** - _ComputeVisibilityRepresentation() - - Compute vertex positions - Compute edge positions - Assign horizontal ranges of vertices - Assign vertical ranges of edges - - ********************************************************************/ - -int _ComputeVisibilityRepresentation(DrawPlanarContext *context) -{ - if (sp_NonEmpty(context->theGraph->edgeHoles)) - return NOTOK; - - if (_ComputeVertexPositions(context) != OK) - return NOTOK; - - if (_ComputeEdgePositions(context) != OK) - return NOTOK; - - if (_ComputeVertexRanges(context) != OK) - return NOTOK; - - if (_ComputeEdgeRanges(context) != OK) - return NOTOK; - - return OK; -} - -/******************************************************************** - _ComputeVertexPositions() - - Computes the vertex positions in the graph. This method accounts - for disconnected graphs by finding the DFS tree roots and then, - for each, invoking _ComputeVertexPositionsInComponent(). - The index variable for the positioning is maintained by this method - so that the vertices in separate components still get distinct - vertex positions. - ********************************************************************/ - -int _ComputeVertexPositions(DrawPlanarContext *context) -{ - graphP theEmbedding = context->theGraph; - int v, vertpos; - - vertpos = 0; - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - // For each DFS tree root in the embedding, we - // compute the vertex positions - if (gp_IsDFSTreeRoot(theEmbedding, v)) - { - if (_ComputeVertexPositionsInComponent(context, v, &vertpos) != OK) - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - _ComputeVertexPositionsInComponent() - - The vertical positions of the vertices are computed based in part - on the information compiled during the planar embedding. - - Each vertex is marked as being between its parent and some ancestor - or beyond the parent relative to the ancestor. The localized, - intuitive notion is that the vertex is either below the parent - or above the parent, but the bicomp containing the vertex, its - parent and the ancestor may be turned upside-down as the result - of a global sequence of operations, resulting in a between or beyond - generalization. - - As the core planarity algorithm constructs successively larger - bicomps out of smaller ones, the bicomp root and its DFS child - are marked as 'tied' in vertex position using markers along the - external face. The marking of the DFS child may be indirect. - Since the child may not be on the external face, its descendant - that is next along the external face is marked instead. - - Later (possibly in the same step or possibly many vertices later), - the Walkdown proceeds around the bicomp and returns to each merge - point, and the tie is broken based on the direction of approach. - - As the Walkdown passes a vertex to its successor, the external - face is short-circuited to remove the vertex from it. Immediately - before this occurs, the new drawing method resolves the tie. Since - the vertex is going to the internal face, its vertex position should - be 'between' its successor and the current vertex being processed - by the Walkdown. - - If the vertex is a child of its external face successor, then it - is simply marked as being 'between' that successor and the current - vertex being processed by the planarity method. But if the vertex - is the parent of its external face successor, then the successor - is placed 'beyond' the vertex. Recall that the successor is either - the DFS child of the vertex or a descendant of that DFS child that - was specially marked because it, not the DFS child, was on the - external face. - - This explains the information that has been collected by the - planarity embedder, which will now be turned into a vertex ordering - system. The idea is to proceed with a pre-order traversal of - the DFS tree, determining the relative orders of the ancestors of - a vertex by the time we get to a vertex. This will allow us to - convert between/beyond into above/below based on the known relative - order of the parent and some given ancestor of the vertex. A vertex - would then be added immediately above or below its parent in the - total ordering, and then the algorithm proceeds to the descendants. - - Consider a depth-first pre-order visitation of vertices. If the - full order of all vertices visited so far is dynamically maintained, - then it is easy to decide whether a vertex goes above or below - its parent based on the between/beyond indicator and the relative - positions in the order of the parent and given ancestor of the - vertex. If the ancestor is above the parent, then 'between' means - put the vertex immediately above its parent and 'beyond' means put - the vertex immediately below its parent in the order. And if the - ancestor is below the parent, then the meaning of between and - beyond are simply reversed. - - Once a vertex is known to be above or below its parent, the drawing - flag is changed from between/beyond to above/below, and processing - proceeds to the next vertex in pre-order depth first search. - - The difficulty lies in keeping an up-to-date topological ordering - that can be queried in constant time to find the relative positions - of two vertices. By itself, this is an instance of "online" or - dynamic topological sorting and has been proven not to be achievable - in linear total time. But this is a special case of the problem and - is therefore solvable through the collection and maintenance of some - additional information. - - Recall that the ancestor V of a vertex is recorded when the setting - for between/beyond is made for a vertex. However, the Walkdown is - invoked on the bicomp rooted by edge (V', C), so the child C of V - that roots the subtree containing the vertex being marked is known. - - Note that when a DFS child is placed above its parent, the entire - DFS subtree of vertices is placed above the parent. Hence, to - determine whether the parent P of a vertex W is above the ancestor - V, where W is marked either between or beyond P and V, we need - only determine the relationship between V and C, which has already - been directly determined due to previous steps of the algorithm - (because V and C are ancestors of P and W). If C is above/below V - then so is P. - - As mentioned above, once the position of P is known relative to V, - it is a simple matter to decide whether to put W above or below P - based on the between/beyond indicator stored in W during embedding. - ********************************************************************/ - -int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pVertpos) -{ -graphP theEmbedding = context->theGraph; -listCollectionP theOrder = LCNew(gp_PrimaryVertexIndexBound(theEmbedding)); -int W, P, C, V, e; - - if (theOrder == NULL) - return NOTOK; - - // Determine the vertex order using a depth first search with - // pre-order visitation. - - sp_ClearStack(theEmbedding->theStack); - sp_Push(theEmbedding->theStack, root); - while (!sp_IsEmpty(theEmbedding->theStack)) - { - sp_Pop(theEmbedding->theStack, W); - - P = gp_GetVertexParent(theEmbedding, W); - V = context->VI[W].ancestor; - C = context->VI[W].ancestorChild; - - // For the special case that we just popped the DFS tree root, - // we simply add the root to its own position. - if (gp_IsNotVertex(P)) - { - // Put the DFS root in the list by itself - LCAppend(theOrder, NIL, W); - // The children of the DFS root have the root as their - // ancestorChild and 'beyond' as the drawingFlag, so this - // causes the root's children to be placed below the root - context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; - } - - // Determine vertex W position relative to P - else - { - // An unresolved tie is an error - if (context->VI[W].drawingFlag == DRAWINGFLAG_TIE) - return NOTOK; - - // If W is the child of a DFS root, then there is no vertex C - // between it and some ancestor V. Both V and C are not a vertex, - // and W will simply take the default of being below its parent. - // If C is a vertex, then it has already been absolutely positioned - // and can be used to help position W relative to its parent P, - // which is equal to or descendant to C. If C below V, then P below V, - // so interpret 'W between P and V' as 'W above P', and interpret - // 'W beyond P relative to V' as 'W below P'. - if (gp_IsNotVertex(C) || context->VI[C].drawingFlag == DRAWINGFLAG_BELOW) - { - if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN) - context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE; - else - context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; - } - - // If C above V, then P above V, so interpret W between - // P and V as W below P, and interpret W beyond P relative - // to V as W above P. - else - { - if (context->VI[W].drawingFlag == DRAWINGFLAG_BETWEEN) - context->VI[W].drawingFlag = DRAWINGFLAG_BELOW; - else - context->VI[W].drawingFlag = DRAWINGFLAG_ABOVE; - } - - if (context->VI[W].drawingFlag == DRAWINGFLAG_BELOW) - LCInsertAfter(theOrder, P, W); - else - LCInsertBefore(theOrder, P, W); - } - - // Push DFS children - e = gp_GetFirstArc(theEmbedding, W); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theEmbedding, e) == EDGE_TYPE_CHILD) - sp_Push(theEmbedding->theStack, gp_GetNeighbor(theEmbedding, e)); - - e = gp_GetNextArc(theEmbedding, e); - } - } - - // Use the order to assign vertical positions - V = root; - while (gp_IsVertex(V)) - { - context->VI[V].pos = *pVertpos; - (*pVertpos)++; - V = LCGetNext(theOrder, root, V); - } - - // Clean up and return - - LCFree(&theOrder); - return OK; -} - - -#ifdef LOGGING -/******************************************************************** - _LogEdgeList() - Used to show the progressive calculation of the edge position list. - ********************************************************************/ -void _LogEdgeList(graphP theEmbedding, listCollectionP edgeList, int edgeListHead) -{ - int eIndex = edgeListHead, e, eTwin; - - gp_Log("EdgeList: [ "); - - while (gp_IsArc(eIndex)) - { - e = (eIndex << 1); - eTwin = gp_GetTwinArc(theEmbedding, e); - - gp_Log(gp_MakeLogStr2("(%d, %d) ", - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eTwin)))); - - eIndex = LCGetNext(edgeList, edgeListHead, eIndex); - } - - gp_LogLine("]"); -} -#endif - -/******************************************************************** - _ComputeEdgePositions() - - Performs a vertical sweep of the combinatorial planar embedding, - developing the edge order in the horizontal sweep line as it - advances through the vertices according to their assigned - vertical positions. - - The 'visitedInfo' member of each vertex is used to indicate the - location in the edge order list of the generator edge for the vertex. - The generator edge is the first edge used to visit the vertex from - a higher vertex in the drawing (i.e. a vertex with an earlier, or - lower, position number). - - All edges added from this vertex to the neighbors below it are - added immediately after the generator edge for the vertex. - ********************************************************************/ - -int _ComputeEdgePositions(DrawPlanarContext *context) -{ -graphP theEmbedding = context->theGraph; -int *vertexOrder = NULL; -listCollectionP edgeList = NULL; -int edgeListHead, edgeListInsertPoint; -int e, eTwin, eCur, v, vpos, epos, eIndex; - - gp_LogLine("\ngraphDrawPlanar.c/_ComputeEdgePositions() start"); - - // Sort the vertices by vertical position (in linear time) - - if ((vertexOrder = (int *) malloc(theEmbedding->N * sizeof(int))) == NULL) { - return NOTOK; - } - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - vertexOrder[context->VI[v].pos] = v; - - // Allocate the edge list of size M. - // This is an array of (prev, next) pointers. - // An edge at position X corresponds to the edge - // at position X in the graph structure, which is - // represented by a pair of adjacent edge records - // at index 2X. - - if (theEmbedding->M > 0 && (edgeList = LCNew(gp_GetFirstEdge(theEmbedding)/2+theEmbedding->M)) == NULL) - { - free(vertexOrder); - return NOTOK; - } - - edgeListHead = NIL; - - // Each vertex starts out with a NIL generator edge. - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - gp_SetVertexVisitedInfo(theEmbedding, v, NIL); - - // Perform the vertical sweep of the combinatorial embedding, using - // the vertex ordering to guide the sweep. - // For each vertex, each edge leading to a vertex with a higher number in - // the vertex order is recorded as the "generator edge", or the edge of - // first discovery of that higher numbered vertex, unless the vertex already has - // a recorded generator edge - for (vpos = 0; vpos < theEmbedding->N; vpos++) - { - // Get the vertex associated with the position - v = vertexOrder[vpos]; - gp_LogLine(gp_MakeLogStr3("Processing vertex %d with DFI=%d at position=%d", - gp_GetVertexIndex(theEmbedding, v), v, vpos)); - - // The DFS tree root of a connected component is always the least - // number vertex in the vertex ordering. We have to give it a - // false generator edge so that it is still "visited" and then - // all of its edges are generators for its neighbor vertices because - // they all have greater numbers in the vertex order. - if (gp_IsDFSTreeRoot(theEmbedding, v)) - { - // Set a false generator edge, so the vertex is distinguishable from - // a vertex with no generator edge when its neighbors are visited - // This way, an edge from a neighbor won't get recorded as the - // generator edge of the DFS tree root. - gp_SetVertexVisitedInfo(theEmbedding, v, NIL - 1); - - // Now we traverse the adjacency list of the DFS tree root and - // record each edge as the generator edge of the neighbors - e = gp_GetFirstArc(theEmbedding, v); - while (gp_IsArc(e)) - { - eIndex = (e >> 1); // div by 2 since each edge is a pair of arcs - - edgeListHead = LCAppend(edgeList, edgeListHead, eIndex); - gp_LogLine(gp_MakeLogStr2("Append generator edge (%d, %d) to edgeList", - gp_GetVertexIndex(theEmbedding, v), gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)))); - - // Set the generator edge for the root's neighbor - gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, e), e); - - // Go to the next node of the root's adj list - e = gp_GetNextArc(theEmbedding, e); - } - } - - // Else, if we are not on a DFS tree root... - else - { - // Get the generator edge of the vertex - // Note that this never gets the false generator edge of a DFS tree root - eTwin = gp_GetVertexVisitedInfo(theEmbedding, v); - if (gp_IsNotArc(eTwin)) - return NOTOK; - e = gp_GetTwinArc(theEmbedding, eTwin); - - // Traverse the edges of the vertex, starting - // from the generator edge and going counterclockwise... - - eIndex = (e >> 1); - edgeListInsertPoint = eIndex; - - eCur = gp_GetNextArcCircular(theEmbedding, e); - while (eCur != e) - { - // If the neighboring vertex's position is greater - // than the current vertex (meaning it is lower in the - // diagram), then add that edge to the edge order. - - if (context->VI[gp_GetNeighbor(theEmbedding, eCur)].pos > vpos) - { - eIndex = eCur >> 1; - LCInsertAfter(edgeList, edgeListInsertPoint, eIndex); - - gp_LogLine(gp_MakeLogStr4("Insert (%d, %d) after (%d, %d)", - gp_GetVertexIndex(theEmbedding, v), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, e)))); - - edgeListInsertPoint = eIndex; - - // If the vertex does not yet have a generator edge, then set it. - // Note that a DFS tree root has a false generator edge, so this if - // test avoids setting a generator edge for a DFS tree root - if (gp_IsNotArc(gp_GetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)))) - { - gp_SetVertexVisitedInfo(theEmbedding, gp_GetNeighbor(theEmbedding, eCur), eCur); - gp_LogLine(gp_MakeLogStr2("Generator edge (%d, %d)", - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, gp_GetTwinArc(theEmbedding, e))), - gp_GetVertexIndex(theEmbedding, gp_GetNeighbor(theEmbedding, eCur)))); - } - } - - // Go to the next node in v's adjacency list - eCur = gp_GetNextArcCircular(theEmbedding, eCur); - } - } - -#ifdef LOGGING - _LogEdgeList(theEmbedding, edgeList, edgeListHead); -#endif - } - - // Now iterate through the edgeList and assign positions to the edges. - epos = 0; - eIndex = edgeListHead; - while (gp_IsArc(eIndex)) - { - e = (eIndex << 1); - eTwin = gp_GetTwinArc(theEmbedding, e); - - context->E[e].pos = context->E[eTwin].pos = epos; - - epos++; - - eIndex = LCGetNext(edgeList, edgeListHead, eIndex); - } - - // Clean up and return - LCFree(&edgeList); - free(vertexOrder); - - gp_LogLine("graphDrawPlanar.c/_ComputeEdgePositions() end\n"); - - return OK; -} - -/******************************************************************** - _ComputeVertexRanges() - - Assumes edge positions are known (see _ComputeEdgePositions()). - A vertex spans horizontally the positions of the edges incident - to it. - ********************************************************************/ - -int _ComputeVertexRanges(DrawPlanarContext *context) -{ - graphP theEmbedding = context->theGraph; - int v, e, min, max; - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - min = theEmbedding->M + 1; - max = -1; - - // Iterate the edges, except in the isolated vertex case we just - // set the min and max to 1 since there no edges controlling where - // it gets drawn. - e = gp_GetFirstArc(theEmbedding, v); - if (gp_IsNotArc(e)) - { - min = max = 0; - } - else - { - while (gp_IsArc(e)) - { - if (min > context->E[e].pos) - min = context->E[e].pos; - - if (max < context->E[e].pos) - max = context->E[e].pos; - - e = gp_GetNextArc(theEmbedding, e); - } - } - - context->VI[v].start = min; - context->VI[v].end = max; - } - - return OK; -} - -/******************************************************************** - _ComputeEdgeRanges() - - Assumes vertex positions are known (see _ComputeVertexPositions()). - An edges spans the vertical range of its endpoints. - ********************************************************************/ - -int _ComputeEdgeRanges(DrawPlanarContext *context) -{ - graphP theEmbedding = context->theGraph; - int e, eTwin, EsizeOccupied, v1, v2, pos1, pos2; - - // Deleted edges are not supported, nor should they be in the embedding, so - // this is just a reality check that avoids an in-use test inside the loop - if (sp_NonEmpty(theEmbedding->edgeHoles)) - return NOTOK; - - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - eTwin = gp_GetTwinArc(theEmbedding, e); - - v1 = gp_GetNeighbor(theEmbedding, e); - v2 = gp_GetNeighbor(theEmbedding, eTwin); - - pos1 = context->VI[v1].pos; - pos2 = context->VI[v2].pos; - - if (pos1 < pos2) - { - context->E[e].start = pos1; - context->E[e].end = pos2; - } - else - { - context->E[e].start = pos2; - context->E[e].end = pos1; - } - - context->E[eTwin].start = context->E[e].start; - context->E[eTwin].end = context->E[e].end; - } - - return OK; -} - -/******************************************************************** - _GetNextExternalFaceVertex() - Uses the extFace links to traverse to the next vertex on the external - face given a current vertex and the link that points to its predecessor. - ********************************************************************/ -int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink) -{ - int nextVertex = gp_GetExtFaceVertex(theGraph, curVertex, 1 ^ *pPrevLink); - - // If the two links in the new vertex are not equal, then only one points - // back to the current vertex, and it is the new prev link. - // Otherwise, the vertex is in a consistently oriented single-edge bicomp, so - // no adjustment of the prev link is needed (due to the consistent orientation). - if (gp_GetExtFaceVertex(theGraph, nextVertex, 0) != gp_GetExtFaceVertex(theGraph, nextVertex, 1)) - { - *pPrevLink = gp_GetExtFaceVertex(theGraph, nextVertex, 0)==curVertex ? 0 : 1; - } - - return nextVertex; -} - -/******************************************************************** - _CollectDrawingData() - To be called by core planarity Walkdown immediately before merging - bicomps and embedding a new back edge. - - Each bicomp is rooted by a DFS tree edge. The parent vertex in - that edge is the bicomp root, and the bicomp contains one DFS child - of the vertex, which is on the child end of the 'root edge'. - - Here we decide whether the DFS child is to be embedded between or - beyond its parent relative to vertex v, the one currently being - processed (and the ancestor endpoint of a back edge being embedded, - where the descendant endpoint is also an endpoint of the bicomp - root being merged). - ********************************************************************/ - -void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink) -{ -graphP theEmbedding = context->theGraph; -int K, Parent, BicompRoot, DFSChild, direction, descendant; - - gp_LogLine("\ngraphDrawPlanar.c/_CollectDrawingData() start"); - gp_LogLine(gp_MakeLogStr3("_CollectDrawingData(RootVertex=%d, W=%d, W_in=%d)", - RootVertex, W, WPrevLink)); - - /* Process all of the merge points to set their drawing flags. */ - - for (K = 0; K < sp_GetCurrentSize(theEmbedding->theStack); K += 4) - { - /* Get the parent and child that are about to be merged from - the 4-tuple in the merge stack */ - Parent = theEmbedding->theStack->S[K]; - BicompRoot = theEmbedding->theStack->S[K+2]; - DFSChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot); - - /* We get the active descendant vertex in the child bicomp that - will be adjacent to the parent along the external face. - This vertex is guaranteed to be found in one step - due to external face 'short-circuiting' that was done in - step 'Parent' of the planarity algorithm. - We pass theEmbedding->N for the second parameter because - of this; we use this function to signify need of extFace - links in the other implementation.*/ - - direction = theEmbedding->theStack->S[K+3]; - descendant = _GetNextExternalFaceVertex(theEmbedding, BicompRoot, &direction); - - /* Now we set the tie flag in the DFS child, and mark the - descendant and parent with non-NIL pointers to the child - whose tie flag is to be resolved as soon as one of the - two is connected to by an edge or child bicomp merge. */ - - context->VI[DFSChild].drawingFlag = DRAWINGFLAG_TIE; - - context->VI[descendant].tie[direction] = DFSChild; - - direction = theEmbedding->theStack->S[K+1]; - context->VI[Parent].tie[direction] = DFSChild; - - gp_LogLine(gp_MakeLogStr5("V[Parent=%d]=.tie[%d] = V[descendant=%d].tie[%d] = (child=%d)", - Parent, direction, descendant, theEmbedding->theStack->S[K+3], DFSChild)); - } - - gp_LogLine("graphDrawPlanar.c/_CollectDrawingData() end\n"); -} - -/******************************************************************** - _BreakTie() - - The given vertex W has just been arrived at by the core planarity - algorithm. Using WPrevLink, we seek its predecessor WPred on the - external face and test whether the two are involved in a tie that - can be resolved. - - Since the planarity algorithm has just passed by WPred, it is - safe to conclude that WPred can go between W and the current vertex. - - Of course, if W was the parent to some DFS child whose subtree - contains WPred, then the DFS child is marked 'between', placing - the whole subtree including WPred between W and the current vertex. - On the other hand, if WPred was the parent of some DFS child whose - subtree contained W, then we achieve the same effect of putting WPred - 'between' W and the curent vertex by marking the DFS child 'beyond'. - Since the DFS child and hence W are beyond W relative to the current - vertex, WPred is also between W and the current vertex. - - Thus the certain positional relationship between W and WPred - relative to a specific ancestor, the current vertex, is used to - indirectly break the positional tie between MIN(W, WPred) and the - DFS child of MIN(W, WPred) whose subtree contains MAX(W, WPred). - - The ancestorChild is the DFS child of the current vertex whose DFS - subtree contains W and WPred, and it is recorded here in order to - optimize the post-processing calculation of vertex positions. - ********************************************************************/ - -int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink) -{ -graphP theEmbedding = context->theGraph; - - /* First we get the predecessor of W. */ - -int WPredNextLink = 1^WPrevLink, - WPred = _GetNextExternalFaceVertex(theEmbedding, W, &WPredNextLink); - - gp_LogLine("\ngraphDrawPlanar.c/::_BreakTie() start"); - gp_LogLine(gp_MakeLogStr4("_BreakTie(BicompRoot=%d, W=%d, W_in=%d) WPred=%d", - BicompRoot, W, WPrevLink, WPred)); - - /* Ties happen only within a bicomp (i.e. between two non-root vertices) */ - if (gp_IsVirtualVertex(theEmbedding, W) || gp_IsVirtualVertex(theEmbedding, WPred)) - { - gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n"); - return OK; - } - - /* The two vertices are either tied or not; having one tied and the other - not is an error */ - - if (context->VI[W].tie[WPrevLink] != context->VI[WPred].tie[WPredNextLink]) - return NOTOK; - - /* If there is a tie, it can now be resolved. */ - if (gp_IsVertex(context->VI[W].tie[WPrevLink])) - { - int DFSChild = context->VI[W].tie[WPrevLink]; - - /* Set the two ancestor variables that contextualize putting W 'between' - or 'beyond' its parent relative to what. */ - - context->VI[DFSChild].ancestorChild = gp_GetDFSChildFromRoot(theEmbedding, BicompRoot); - context->VI[DFSChild].ancestor = gp_GetPrimaryVertexFromRoot(theEmbedding, BicompRoot); - - gp_LogLine(gp_MakeLogStr4("V[child=%d]=.ancestorChild = %d, V[child=%d]=.ancestor = %d", - DFSChild, context->VI[DFSChild].ancestorChild, DFSChild, context->VI[DFSChild].ancestor)); - - /* If W is the ancestor of WPred, then the DFSChild subtree contains - WPred, and so must go between W and some ancestor. */ - if (W < WPred) - { - context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BETWEEN; - gp_LogLine(gp_MakeLogStr3("Child=%d is 'between' ancestorChild=%d and ancestor=%d", - DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor)); - } - - /* If W is the descendant, so we achieve the effect of putting WPred - between DFSChild and ancestor by putting the DFSChild 'beyond' WPred. */ - else - { - context->VI[DFSChild].drawingFlag = DRAWINGFLAG_BEYOND; - gp_LogLine(gp_MakeLogStr3("Child=%d is 'beyond' ancestorChild=%d relative to ancestor=%d", - DFSChild, context->VI[DFSChild].ancestorChild, context->VI[DFSChild].ancestor)); - } - - /* The tie is resolved so clear the flags*/ - context->VI[W].tie[WPrevLink] = NIL; - context->VI[WPred].tie[WPredNextLink] = NIL; - } - - gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n"); - return OK; -} - -/******************************************************************** - _RenderToString() - Draws the previously calculated visibility representation in a - string of size (M+1)*2N + 1 characters, which should be deallocated - with free(). - - Returns NULL on failure, or the string containing the visibility - representation otherwise. The string can be printed using %s, - ********************************************************************/ - -char *_RenderToString(graphP theEmbedding) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theEmbedding, DRAWPLANAR_ID, (void *) &context); - - if (context != NULL) - { - int N = theEmbedding->N; - int M = theEmbedding->M; - int zeroBasedVertexOffset = (theEmbedding->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theEmbedding) : 0; - int n, m, EsizeOccupied, v, vRange, e, eRange, Mid, Pos; - char *visRep = (char *) malloc(sizeof(char) * ((M+1) * 2*N + 1)); - char numBuffer[32]; - - if (visRep == NULL) - return NULL; - - if (sp_NonEmpty(context->theGraph->edgeHoles)) - { - free(visRep); - return NULL; - } - - // Clear the space - for (n = 0; n < N; n++) - { - for (m=0; m < M; m++) - { - visRep[(2*n) * (M+1) + m] = ' '; - visRep[(2*n+1) * (M+1) + m] = ' '; - } - - visRep[(2*n) * (M+1) + M] = '\n'; - visRep[(2*n+1) * (M+1) + M] = '\n'; - } - - // Draw the vertices - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - Pos = context->VI[v].pos; - for (vRange=context->VI[v].start; vRange <= context->VI[v].end; vRange++) - visRep[(2*Pos) * (M+1) + vRange] = '-'; - - // Draw vertex label - Mid = (context->VI[v].start + context->VI[v].end) / 2; - sprintf(numBuffer, "%d", v - zeroBasedVertexOffset); - if ((unsigned)(context->VI[v].end - context->VI[v].start + 1) >= strlen(numBuffer)) - { - memcpy((char *) visRep + (2*Pos) * (M+1) + Mid, (char *) numBuffer, strlen(numBuffer)); - } - // If the vertex width is less than the label width, then fail gracefully - else - { - if (strlen(numBuffer)==2) - visRep[(2*Pos) * (M+1) + Mid] = numBuffer[0]; - else - visRep[(2*Pos) * (M+1) + Mid] = '*'; - - visRep[(2*Pos+1) * (M+1) + Mid] = numBuffer[strlen(numBuffer)-1]; - } - } - - // Draw the edges - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - Pos = context->E[e].pos; - for (eRange=context->E[e].start; eRange < context->E[e].end; eRange++) - { - if (eRange > context->E[e].start) - visRep[(2*eRange) * (M+1) + Pos] = '|'; - visRep[(2*eRange+1) * (M+1) + Pos] = '|'; - } - } - - // Null terminate string and return it - visRep[(M+1) * 2*N] = '\0'; - return visRep; - } - - return NULL; -} - -/******************************************************************** - gp_DrawPlanar_RenderToFile() - Creates a rendition of the planar graph visibility representation - as a string, then dumps the string to the file. - ********************************************************************/ -int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName) -{ - if (sp_IsEmpty(theEmbedding->edgeHoles)) - { - FILE *outfile; - char *theRendition; - - if (strcmp(theFileName, "stdout") == 0) - outfile = stdout; - else if (strcmp(theFileName, "stderr") == 0) - outfile = stderr; - else outfile = fopen(theFileName, WRITETEXT); - - if (outfile == NULL) - return NOTOK; - - theRendition = _RenderToString(theEmbedding); - if (theRendition != NULL) - { - fprintf(outfile, "%s", theRendition); - free(theRendition); - } - - if (strcmp(theFileName, "stdout") == 0 || strcmp(theFileName, "stderr") == 0) - fflush(outfile); - - else if (fclose(outfile) != 0) - return NOTOK; - - return theRendition ? OK : NOTOK; - } - - return NOTOK; -} - -/******************************************************************** - _CheckVisibilityRepresentationIntegrity() - ********************************************************************/ - -int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context) -{ -graphP theEmbedding = context->theGraph; -int v, e, eTwin, EsizeOccupied, epos, eposIndex; - - if (sp_NonEmpty(context->theGraph->edgeHoles)) - return NOTOK; - - _ClearVisitedFlags(theEmbedding); - -/* Test whether the vertex values make sense and - whether the vertex positions are unique. */ - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - if (theEmbedding->M > 0) - { - if (context->VI[v].pos < 0 || - context->VI[v].pos >= theEmbedding->N || - context->VI[v].start < 0 || - context->VI[v].start > context->VI[v].end || - context->VI[v].end >= theEmbedding->M) - return NOTOK; - } - - // Has the vertex position been used by a vertex before vertex v? - if (gp_GetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding))) - return NOTOK; - - // Mark the vertex position as used by vertex v. - // Note that this marking is made on some other vertex unrelated to v - // We're just reusing the vertex visited array as cheap storage for a - // detector of reusing vertex position integers. - gp_SetVertexVisited(theEmbedding, context->VI[v].pos + gp_GetFirstVertex(theEmbedding)); - } - -/* Test whether the edge values make sense and - whether the edge positions are unique */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - /* Each edge has two index locations in the edge information array */ - eTwin = gp_GetTwinArc(theEmbedding, e); - - if (context->E[e].pos != context->E[eTwin].pos || - context->E[e].start != context->E[eTwin].start || - context->E[e].end != context->E[eTwin].end || - context->E[e].pos < 0 || - context->E[e].pos >= theEmbedding->M || - context->E[e].start < 0 || - context->E[e].start > context->E[e].end || - context->E[e].end >= theEmbedding->N) - return NOTOK; - - /* Get the recorded horizontal position of that edge, - a number between 0 and M-1 */ - - epos = context->E[e].pos; - - /* Convert that to an index in the graph structure so we - can use the visited flags in the graph's edges to - tell us whether the positions are being reused. */ - - eposIndex = (epos<<1) + gp_GetFirstEdge(theEmbedding); - eTwin = gp_GetTwinArc(theEmbedding, eposIndex); - - if (gp_GetEdgeVisited(theEmbedding, eposIndex) || gp_GetEdgeVisited(theEmbedding, eTwin)) - return NOTOK; - - gp_SetEdgeVisited(theEmbedding, eposIndex); - gp_SetEdgeVisited(theEmbedding, eTwin); - } - -/* Test whether any edge intersects any vertex position - for a vertex that is not an endpoint of the edge. */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theEmbedding); - for (e = gp_GetFirstEdge(theEmbedding); e < EsizeOccupied; e+=2) - { - eTwin = gp_GetTwinArc(theEmbedding, e); - - for (v = gp_GetFirstVertex(theEmbedding); gp_VertexInRange(theEmbedding, v); v++) - { - /* If the vertex is an endpoint of the edge, then... */ - - if (gp_GetNeighbor(theEmbedding, e) == v || gp_GetNeighbor(theEmbedding, eTwin) == v) - { - /* The vertical position of the vertex must be - at the top or bottom of the edge, */ - if (context->E[e].start != context->VI[v].pos && - context->E[e].end != context->VI[v].pos) - return NOTOK; - - /* The horizontal edge position must be in the range of the vertex */ - if (context->E[e].pos < context->VI[v].start || - context->E[e].pos > context->VI[v].end) - return NOTOK; - } - - /* If the vertex is not an endpoint of the edge... */ - - else // if (gp_GetNeighbor(theEmbedding, e) != v && gp_GetNeighbor(theEmbedding, eTwin) != v) - { - /* If the vertical position of the vertex is in the - vertical range of the edge ... */ - - if (context->E[e].start <= context->VI[v].pos && - context->E[e].end >= context->VI[v].pos) - { - /* And if the horizontal position of the edge is in the - horizontal range of the vertex, then return an error. */ - - if (context->VI[v].start <= context->E[e].pos && - context->VI[v].end >= context->E[e].pos) - return NOTOK; - } - } - } - } - - -/* All tests passed */ - - return OK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.h deleted file mode 100644 index 23f6d914a..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef GRAPH_DRAWPLANAR_H -#define GRAPH_DRAWPLANAR_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graphStructures.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define DRAWPLANAR_NAME "DrawPlanar" - -int gp_AttachDrawPlanar(graphP theGraph); -int gp_DetachDrawPlanar(graphP theGraph); - -int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.private.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.private.h deleted file mode 100644 index 2034c6782..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar.private.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef GRAPH_DRAWPLANAR_PRIVATE_H -#define GRAPH_DRAWPLANAR_PRIVATE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Additional equipment for each edge -/* - pos, start, end: used to store a visibility representation, or - horvert diagram of a planar graph. - For edges, horizontal position, vertical range -*/ -typedef struct -{ - int pos, start, end; -} DrawPlanar_EdgeRec; - -typedef DrawPlanar_EdgeRec * DrawPlanar_EdgeRecP; - -// Additional equipment for each vertex -/* - pos, start, end: used to store a visibility representation, or - horvert diagram, of a planar graph. - For vertices, vertical position, horizontal range - drawingFlag, ancestor, ancestorChild: used to collect information needed - to help 'draw' a visibility representation. During planar - embedding, a vertex is determined to be between its DFS parent and - a given ancestor (the vertex being processed) or beyond the parent - relative to the ancestor. In post processing, the relative - orientation of the parent and ancestor are determined, - then the notion of between/beyond resolves to above/below or - below/above depending on whether the ancestor is above or below, - respectively, the parent. The ancestorChild are used to help r - esolve this latter question. - tie[2] stores information along the external face during embedding - that is pertinent to helping break ties in the decisions about - vertical vertex positioning. When vertices are first merged - together into a bicomp, we cannot always decide right away which - vertices will be above or below others. But as we traverse the - external face removing inactive vertices, these positional ties - can be resolved. -*/ -typedef struct -{ - int pos, start, end; - int drawingFlag, ancestor, ancestorChild; - int tie[2]; -} DrawPlanar_VertexInfo; - -typedef DrawPlanar_VertexInfo * DrawPlanar_VertexInfoP; - -#define DRAWINGFLAG_BEYOND 0 -#define DRAWINGFLAG_TIE 1 -#define DRAWINGFLAG_BETWEEN 2 -#define DRAWINGFLAG_BELOW 3 -#define DRAWINGFLAG_ABOVE 4 - -typedef struct -{ - // Helps distinguish initialize from re-initialize - int initialized; - - // The graph that this context augments - graphP theGraph; - - // Parallel array for additional edge level equipment - DrawPlanar_EdgeRecP E; - - // Parallel array for additional vertex level equipment - DrawPlanar_VertexInfoP VI; - - // Overloaded function pointers - graphFunctionTable functions; - -} DrawPlanarContext; - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar_Extensions.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar_Extensions.c deleted file mode 100644 index 3cb27b1b2..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphDrawPlanar_Extensions.c +++ /dev/null @@ -1,696 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -#include - -#include "graphDrawPlanar.private.h" -#include "graphDrawPlanar.h" - -extern void _ClearVertexVisitedFlags(graphP theGraph, int); - -extern void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink); -extern int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink); - -extern int _ComputeVisibilityRepresentation(DrawPlanarContext *context); -extern int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context); - -/* Forward declarations of local functions */ - -void _DrawPlanar_ClearStructures(DrawPlanarContext *context); -int _DrawPlanar_CreateStructures(DrawPlanarContext *context); -int _DrawPlanar_InitStructures(DrawPlanarContext *context); - -void _DrawPlanar_InitEdgeRec(DrawPlanarContext *context, int v); -void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v); - -/* Forward declarations of overloading functions */ - -int _DrawPlanar_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); -int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); -int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); - -int _DrawPlanar_InitGraph(graphP theGraph, int N); -void _DrawPlanar_ReinitializeGraph(graphP theGraph); -int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); -int _DrawPlanar_SortVertices(graphP theGraph); - -int _DrawPlanar_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize); -int _DrawPlanar_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize); - -/* Forward declarations of functions used by the extension system */ - -void *_DrawPlanar_DupContext(void *pContext, void *theGraph); -void _DrawPlanar_FreeContext(void *); - -/**************************************************************************** - * DRAWPLANAR_ID - the variable used to hold the integer identifier for this - * extension, enabling this feature's extension context to be distinguished - * from other features' extension contexts that may be attached to a graph. - ****************************************************************************/ - -int DRAWPLANAR_ID = 0; - -/**************************************************************************** - gp_AttachDrawPlanar() - - This function adjusts the graph data structure to attach the planar graph - drawing feature. - - To activate this feature during gp_Embed(), use EMBEDFLAGS_DRAWPLANAR. - - This method may be called immediately after gp_New() in the case of - invoking gp_Read(). For generating graphs, gp_InitGraph() can be invoked - before or after this enabling method. This method detects if the core - graph has already been initialized, and if so, it will initialize the - additional data structures specific to planar graph drawing. This makes - it possible to invoke gp_New() and gp_InitGraph() together, and then attach - this feature only if it is requested at run-time. - - Returns OK for success, NOTOK for failure. - ****************************************************************************/ - -int gp_AttachDrawPlanar(graphP theGraph) -{ - DrawPlanarContext *context = NULL; - - // If the drawing feature has already been attached to the graph, - // then there is no need to attach it again - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - if (context != NULL) - { - return OK; - } - - // Allocate a new extension context - context = (DrawPlanarContext *) malloc(sizeof(DrawPlanarContext)); - if (context == NULL) - { - return NOTOK; - } - - // First, tell the context that it is not initialized - context->initialized = 0; - - // Save a pointer to theGraph in the context - context->theGraph = theGraph; - - // Put the overload functions into the context function table. - // gp_AddExtension will overload the graph's functions with these, and - // return the base function pointers in the context function table - memset(&context->functions, 0, sizeof(graphFunctionTable)); - - context->functions.fpMergeBicomps = _DrawPlanar_MergeBicomps; - context->functions.fpHandleInactiveVertex = _DrawPlanar_HandleInactiveVertex; - context->functions.fpEmbedPostprocess = _DrawPlanar_EmbedPostprocess; - context->functions.fpCheckEmbeddingIntegrity = _DrawPlanar_CheckEmbeddingIntegrity; - context->functions.fpCheckObstructionIntegrity = _DrawPlanar_CheckObstructionIntegrity; - - context->functions.fpInitGraph = _DrawPlanar_InitGraph; - context->functions.fpReinitializeGraph = _DrawPlanar_ReinitializeGraph; - context->functions.fpEnsureArcCapacity = _DrawPlanar_EnsureArcCapacity; - context->functions.fpSortVertices = _DrawPlanar_SortVertices; - - context->functions.fpReadPostprocess = _DrawPlanar_ReadPostprocess; - context->functions.fpWritePostprocess = _DrawPlanar_WritePostprocess; - - _DrawPlanar_ClearStructures(context); - - // Store the Draw context, including the data structure and the - // function pointers, as an extension of the graph - if (gp_AddExtension(theGraph, &DRAWPLANAR_ID, (void *) context, - _DrawPlanar_DupContext, _DrawPlanar_FreeContext, - &context->functions) != OK) - { - _DrawPlanar_FreeContext(context); - return NOTOK; - } - - // Create the Draw-specific structures if the size of the graph is known - // Attach functions are typically invoked after gp_New(), but if a graph - // extension must be attached before gp_Read(), then the attachment - // also happens before gp_InitGraph() because gp_Read() invokes init only - // after it reads the order N of the graph. Hence, this attach call would - // occur when N==0 in the case of gp_Read(). - // But if a feature is attached after gp_InitGraph(), then N > 0 and so we - // need to create and initialize all the custom data structures - if (theGraph->N > 0) - { - if (_DrawPlanar_CreateStructures(context) != OK || - _DrawPlanar_InitStructures(context) != OK) - { - _DrawPlanar_FreeContext(context); - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - gp_DetachDrawPlanar() - ********************************************************************/ - -int gp_DetachDrawPlanar(graphP theGraph) -{ - return gp_RemoveExtension(theGraph, DRAWPLANAR_ID); -} - -/******************************************************************** - _DrawPlanar_ClearStructures() - ********************************************************************/ - -void _DrawPlanar_ClearStructures(DrawPlanarContext *context) -{ - if (!context->initialized) - { - // Before initialization, the pointers are stray, not NULL - // Once NULL or allocated, free() or LCFree() can do the job - context->E = NULL; - context->VI = NULL; - - context->initialized = 1; - } - else - { - if (context->E != NULL) - { - free(context->E); - context->E = NULL; - } - if (context->VI != NULL) - { - free(context->VI); - context->VI = NULL; - } - } -} - -/******************************************************************** - _DrawPlanar_CreateStructures() - Create uninitialized structures for the vertex and edge levels, - and initialized structures for the graph level - ********************************************************************/ -int _DrawPlanar_CreateStructures(DrawPlanarContext *context) -{ - graphP theGraph = context->theGraph; - int VIsize = gp_PrimaryVertexIndexBound(theGraph); - int Esize = gp_EdgeIndexBound(theGraph); - - if (theGraph->N <= 0) - return NOTOK; - - if ((context->E = (DrawPlanar_EdgeRecP) malloc(Esize*sizeof(DrawPlanar_EdgeRec))) == NULL || - (context->VI = (DrawPlanar_VertexInfoP) malloc(VIsize*sizeof(DrawPlanar_VertexInfo))) == NULL - ) - { - return NOTOK; - } - - return OK; -} - -/******************************************************************** - _DrawPlanar_InitStructures() - Intended to be called when N>0. - Initializes vertex and edge levels only. Graph level is - already initialized in _CreateStructures() - ********************************************************************/ -int _DrawPlanar_InitStructures(DrawPlanarContext *context) -{ -#if NIL == 0 - memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(DrawPlanar_VertexInfo)); - memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(DrawPlanar_EdgeRec)); -#else - int v, e, Esize; - graphP theGraph = context->theGraph; - - if (theGraph->N <= 0) - return NOTOK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - _DrawPlanar_InitVertexInfo(context, v); - - Esize = gp_EdgeIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - _DrawPlanar_InitEdgeRec(context, e); -#endif - - return OK; -} - -/******************************************************************** - _DrawPlanar_DupContext() - ********************************************************************/ - -void *_DrawPlanar_DupContext(void *pContext, void *theGraph) -{ - DrawPlanarContext *context = (DrawPlanarContext *) pContext; - DrawPlanarContext *newContext = (DrawPlanarContext *) malloc(sizeof(DrawPlanarContext)); - - if (newContext != NULL) - { - int VIsize = gp_PrimaryVertexIndexBound((graphP) theGraph); - int Esize = gp_EdgeIndexBound((graphP) theGraph); - - *newContext = *context; - - newContext->theGraph = (graphP) theGraph; - - newContext->initialized = 0; - _DrawPlanar_ClearStructures(newContext); - if (((graphP) theGraph)->N > 0) - { - if (_DrawPlanar_CreateStructures(newContext) != OK) - { - _DrawPlanar_FreeContext(newContext); - return NULL; - } - - // Initialize custom data structures by copying - memcpy(newContext->E, context->E, Esize*sizeof(DrawPlanar_EdgeRec)); - memcpy(newContext->VI, context->VI, VIsize*sizeof(DrawPlanar_VertexInfo)); - } - } - - return newContext; -} - -/******************************************************************** - _DrawPlanar_FreeContext() - ********************************************************************/ - -void _DrawPlanar_FreeContext(void *pContext) -{ - DrawPlanarContext *context = (DrawPlanarContext *) pContext; - - _DrawPlanar_ClearStructures(context); - free(pContext); -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_InitGraph(graphP theGraph, int N) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context == NULL) { - return NOTOK; - } - - theGraph->N = N; - theGraph->NV = N; - if (theGraph->arcCapacity == 0) - theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N; - - if (_DrawPlanar_CreateStructures(context) != OK || - _DrawPlanar_InitStructures(context) != OK) - return NOTOK; - - context->functions.fpInitGraph(theGraph, N); - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -void _DrawPlanar_ReinitializeGraph(graphP theGraph) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - // Reinitialize the graph - context->functions.fpReinitializeGraph(theGraph); - - // Do the reinitialization that is specific to this module - _DrawPlanar_InitStructures(context); - } -} - -/******************************************************************** - The current implementation does not support an increase of arc - (edge record) capacity once the extension is attached to the graph - data structure. This is only due to not being necessary to support. - For now, it is easy to ensure the correct capacity before attaching - the extension, but support could be added later if there is some - reason to do so. - ********************************************************************/ - -int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_SortVertices(graphP theGraph) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - // If this is a planarity-based algorithm to which graph drawing has been attached, - // and if the embedding process has already been completed - if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) - { - int v, vIndex; - DrawPlanar_VertexInfo temp; - - // Relabel the context data members that indicate vertices - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - if (gp_IsVertex(context->VI[v].ancestor)) - { - context->VI[v].ancestor = gp_GetVertexIndex(theGraph, context->VI[v].ancestor); - context->VI[v].ancestorChild = gp_GetVertexIndex(theGraph, context->VI[v].ancestorChild); - } - } - - // "Sort" the extra vertex info associated with each vertex so that it is rearranged according - // to the index values of the vertices. This could be done very easily with an extra array in - // which, for each v, newVI[index of v] = VI[v]. However, this loop avoids memory allocation - // by performing the operation (almost) in-place, except for the pre-existing visitation flags. - _ClearVertexVisitedFlags(theGraph, FALSE); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - // If the correct data has already been placed into position v - // by prior steps, then skip to the next vertex - if (gp_GetVertexVisited(theGraph, v)) - continue; - - // At the beginning of processing position v, the data in position v - // corresponds to data that belongs at the index of v. - vIndex = gp_GetVertexIndex(theGraph, v); - - // Iterate on position v until it receives the correct data - while (!gp_GetVertexVisited(theGraph, v)) - { - // Place the data at position v into its proper location at position - // vIndex, and move vIndex's data into position v. - temp = context->VI[v]; - context->VI[v] = context->VI[vIndex]; - context->VI[vIndex] = temp; - - // The data at position vIndex is now marked as being correct. - gp_SetVertexVisited(theGraph, vIndex); - - // The data now in position v is the data from position vIndex, - // whose index we now take as the new vIndex - vIndex = gp_GetVertexIndex(theGraph, vIndex); - } - } - } - - if (context->functions.fpSortVertices(theGraph) != OK) - return NOTOK; - - return OK; - } - - return NOTOK; -} - -/******************************************************************** - Returns OK for a successful merge, NOTOK on an internal failure, - or NONEMBEDDABLE if the merge is blocked - ********************************************************************/ - -int _DrawPlanar_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) - { - _CollectDrawingData(context, RootVertex, W, WPrevLink); - } - - return context->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink); - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - int RetVal = context->functions.fpHandleInactiveVertex(theGraph, BicompRoot, pW, pWPrevLink); - - if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) - { - if (_BreakTie(context, BicompRoot, *pW, *pWPrevLink) != OK) - return NOTOK; - } - - return RetVal; - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -void _DrawPlanar_InitEdgeRec(DrawPlanarContext *context, int e) -{ - context->E[e].pos = 0; - context->E[e].start = 0; - context->E[e].end = 0; -} - -/******************************************************************** - ********************************************************************/ - -void _DrawPlanar_InitVertexInfo(DrawPlanarContext *context, int v) -{ - context->VI[v].pos = 0; - context->VI[v].start = 0; - context->VI[v].end = 0; - - context->VI[v].drawingFlag = DRAWINGFLAG_BEYOND; - context->VI[v].ancestorChild = NIL; - context->VI[v].ancestor = NIL; - context->VI[v].tie[0] = NIL; - context->VI[v].tie[1] = NIL; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - int RetVal = context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); - - if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR) - { - if (RetVal == OK) - { - RetVal = _ComputeVisibilityRepresentation(context); - } - } - - return RetVal; - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - if (context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph) != OK) - return NOTOK; - - return _CheckVisibilityRepresentationIntegrity(context); - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) -{ - return OK; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - if (context->functions.fpReadPostprocess(theGraph, extraData, extraDataSize) != OK) - return NOTOK; - - else if (extraData != NULL && extraDataSize > 0) - { - int v, e, tempInt, EsizeOccupied; - char line[64], tempChar; - - sprintf(line, "<%s>", DRAWPLANAR_NAME); - - // Find the start of the data for this feature - extraData = strstr(extraData, line); - if (extraData == NULL) - return NOTOK; - - // Advance past the start tag - extraData = (void *) ((char *) extraData + strlen(line)+1); - - // Read the N lines of vertex information - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar, - &context->VI[v].pos, - &context->VI[v].start, - &context->VI[v].end); - - extraData = strchr(extraData, '\n') + 1; - } - - // Read the lines that contain edge information - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - { - sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar, - &context->E[e].pos, - &context->E[e].start, - &context->E[e].end); - - extraData = strchr(extraData, '\n') + 1; - } - } - - return OK; - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _DrawPlanar_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize) -{ - DrawPlanarContext *context = NULL; - gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context); - - if (context != NULL) - { - if (context->functions.fpWritePostprocess(theGraph, pExtraData, pExtraDataSize) != OK) - return NOTOK; - else - { - int v, e, EsizeOccupied; - char line[64]; - int maxLineSize = 64, extraDataPos = 0; - char *extraData = (char *) malloc((1 + theGraph->N + 2*theGraph->M + 1) * maxLineSize * sizeof(char)); - int zeroBasedVertexOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; - int zeroBasedEdgeOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstEdge(theGraph) : 0; - - if (extraData == NULL) - return NOTOK; - - // Bit of an unlikely case, but for safety, a bigger maxLineSize - // and line array size are needed to handle very large graphs - if (theGraph->N > 2000000000) - { - free(extraData); - return NOTOK; - } - - sprintf(line, "<%s>\n", DRAWPLANAR_NAME); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - sprintf(line, "%d: %d %d %d\n", v-zeroBasedVertexOffset, - context->VI[v].pos, - context->VI[v].start, - context->VI[v].end); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - } - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - { - if (gp_EdgeInUse(theGraph, e)) - { - sprintf(line, "%d: %d %d %d\n", e-zeroBasedEdgeOffset, - context->E[e].pos, - context->E[e].start, - context->E[e].end); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - } - } - - sprintf(line, "\n", DRAWPLANAR_NAME); - strcpy(extraData+extraDataPos, line); - extraDataPos += (int) strlen(line); - - *pExtraData = (void *) extraData; - *pExtraDataSize = extraDataPos * sizeof(char); - } - - return OK; - } - - return NOTOK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphEmbed.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphEmbed.c deleted file mode 100644 index 81f3ec48d..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphEmbed.c +++ /dev/null @@ -1,1378 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include - -#include "graph.h" - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -/* Imported functions */ - -extern void _ClearVertexVisitedFlags(graphP theGraph, int); - -extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); -extern int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R); - -extern void _InitVertexRec(graphP theGraph, int v); - -/* Private functions (some are exported to system only) */ - -int _EmbeddingInitialize(graphP theGraph); - -void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); - -void _InvertVertex(graphP theGraph, int V); -void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R); -int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); - -void _WalkUp(graphP theGraph, int v, int e); -int _WalkDown(graphP theGraph, int v, int RootVertex); - -int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -void _AdvanceFwdArcList(graphP theGraph, int v, int child, int nextChild); - -int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -int _OrientVerticesInEmbedding(graphP theGraph); -int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); -int _JoinBicomps(graphP theGraph); - -/******************************************************************** - gp_Embed() - - Either a planar embedding is created in theGraph, or a Kuratowski - subgraph is isolated. Either way, theGraph remains sorted by DFI - since that is the most common desired result. The original vertex - numbers are available in the 'index' members of the vertex records. - Moreover, gp_SortVertices() can be invoked to put the vertices in - the order of the input graph, at which point the 'index' members of - the vertex records will contain the vertex DFIs. - - return OK if the embedding was successfully created or no subgraph - homeomorphic to a topological obstruction was found. - - NOTOK on internal failure - - NONEMBEDDABLE if the embedding couldn't be created due to - the existence of a subgraph homeomorphic to a - topological obstruction. - - For core planarity, OK is returned when theGraph contains a planar - embedding of the input graph, and NONEMBEDDABLE is returned when a - subgraph homeomorphic to K5 or K3,3 has been isolated in theGraph. - - Extension modules can overload functions used by gp_Embed to achieve - alternate algorithms. In those cases, the return results are - similar. For example, a K3,3 search algorithm would return - NONEMBEDDABLE if it finds the K3,3 obstruction, and OK if the graph - is planar or only contains K5 homeomorphs. Similarly, an - outerplanarity module can return OK for an outerplanar embedding or - NONEMBEDDABLE when a subgraph homeomorphic to K2,3 or K4 has been - isolated. - - The algorithm extension for gp_Embed() is encoded in the embedFlags, - and the details of the return value can be found in the extension - module that defines the embedding flag. - ********************************************************************/ - -int gp_Embed(graphP theGraph, int embedFlags) -{ -int v, e, c; -int RetVal = OK; - - // Basic parameter checks - if (theGraph==NULL) - return NOTOK; - - // Preprocessing - theGraph->embedFlags = embedFlags; - - // Allow extension algorithms to postprocess the DFS - if (theGraph->functions.fpEmbeddingInitialize(theGraph) != OK) - return NOTOK; - - // In reverse DFI order, embed the back edges from each vertex to its DFS descendants. - for (v = gp_GetLastVertex(theGraph); gp_VertexInRangeDescending(theGraph, v); v--) - { - RetVal = OK; - - // Walkup calls establish Pertinence in Step v - // Do the Walkup for each cycle edge from v to a DFS descendant W. - e = gp_GetVertexFwdArcList(theGraph, v); - while (gp_IsArc(e)) - { - theGraph->functions.fpWalkUp(theGraph, v, e); - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, v)) - e = NIL; - } - gp_SetVertexPertinentRootsList(theGraph, v, NIL); - - // Work systematically through the DFS children of vertex v, using Walkdown - // to add the back edges from v to its descendants in each of the DFS subtrees - c = gp_GetVertexSortedDFSChildList(theGraph, v); - while (gp_IsVertex(c)) - { - if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, c))) - { - RetVal = theGraph->functions.fpWalkDown(theGraph, v, gp_GetRootFromDFSChild(theGraph, c)); - // If Walkdown returns OK, then it is OK to proceed with edge addition. - // Otherwise, if Walkdown returns NONEMBEDDABLE then we stop edge addition. - if (RetVal != OK) - break; - } - c = gp_GetVertexNextDFSChild(theGraph, v, c); - } - - // If the Walkdown determined that the graph is NONEMBEDDABLE, - // then the guiding embedder loop can be stopped now. - if (RetVal != OK) - break; - } - - // Postprocessing to orient the embedding and merge any remaining separated bicomps. - // Some extension algorithms may overload this function, e.g. to do nothing if they - // have no need of an embedding. - return theGraph->functions.fpEmbedPostprocess(theGraph, v, RetVal); -} - -/******************************************************************** - _EmbeddingInitialize() - - This method performs the following tasks: - (1) Assign depth first index (DFI) and DFS parentvalues to vertices - (2) Assign DFS edge types - (3) Create a sortedDFSChildList for each vertex, sorted by child DFI - (4) Create a sortedFwdArcList for each vertex, sorted by descendant DFI - (5) Assign leastAncestor values to vertices - (6) Sort the vertices by their DFIs - (7) Initialize for pertinence and future pertinence management - (8) Embed each tree edge as a singleton biconnected component - - The first five of these are performed in a single-pass DFS of theGraph. - Afterward, the vertices are sorted by their DFIs, the lowpoint values - are assigned and then the DFS tree edges stored in virtual vertices - during the DFS are used to create the DFS tree embedding. - ********************************************************************/ -int _EmbeddingInitialize(graphP theGraph) -{ - stackP theStack; - int DFI, v, R, uparent, u, uneighbor, e, f, eTwin, ePrev, eNext; - int leastValue, child; - -#ifdef PROFILE -platform_time start, end; -platform_GetTime(start); -#endif - - gp_LogLine("graphEmbed.c/_EmbeddingInitialize() start\n"); - - theStack = theGraph->theStack; - - // At most we push 2 integers per edge from a vertex to each *unvisited* neighbor - // plus one additional integer to help detect post-processing. This is less - // than the 2 * arcCapacity integer stack that is already in theGraph structure, - // so we make sure it's still there and cleared, then we clear all vertex - // visited flags in prep for the Depth first search operation. */ - - if (sp_GetCapacity(theStack) < 2*gp_GetArcCapacity(theGraph)) - return NOTOK; - - sp_ClearStack(theStack); - - _ClearVertexVisitedFlags(theGraph, FALSE); - - // This outer loop processes each connected component of a disconnected graph - // No need to compare v < N since DFI will reach N when inner loop processes the - // last connected component in the graph - for (DFI = v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, DFI); v++) - { - // Skip numbered vertices to cause the outerloop to find the - // next DFS tree root in a disconnected graph - if (gp_IsVertex(gp_GetVertexParent(theGraph, v))) - continue; - - // DFS a connected component - sp_Push2(theStack, NIL, NIL); - while (sp_NonEmpty(theStack)) - { - sp_Pop2(theStack, uparent, e); - - // For vertex uparent and edge e, obtain the opposing endpoint u of e - // If uparent is NIL, then e is also NIL and we have encountered the - // false edge to the DFS tree root as pushed above. - u = gp_IsNotVertex(uparent) ? v : gp_GetNeighbor(theGraph, e); - - // We popped an edge to an unvisited vertex, so it is either a DFS tree edge - // or a false edge to the DFS tree root (u). - if (!gp_GetVertexVisited(theGraph, u)) - { - gp_LogLine(gp_MakeLogStr3("v=%d, DFI=%d, parent=%d", u, DFI, uparent)); - - // (1) Set the DFI and DFS parent - gp_SetVertexVisited(theGraph, u); - gp_SetVertexIndex(theGraph, u, DFI++); - gp_SetVertexParent(theGraph, u, uparent); - - if (gp_IsArc(e)) - { - // (2) Set the edge type values for tree edges - gp_SetEdgeType(theGraph, e, EDGE_TYPE_CHILD); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, e), EDGE_TYPE_PARENT); - - // (3) Record u in the sortedDFSChildList of uparent - gp_SetVertexSortedDFSChildList(theGraph, uparent, - gp_AppendDFSChild(theGraph, uparent, gp_GetVertexIndex(theGraph, u))); - - // (8) Record e as the first and last arc of the virtual vertex R, - // a root copy of uparent uniquely associated with child u - R = gp_GetRootFromDFSChild(theGraph, gp_GetVertexIndex(theGraph, u)); - gp_SetFirstArc(theGraph, R, e); - gp_SetLastArc(theGraph, R, e); - } - - // (5) Initialize the least ancestor value - gp_SetVertexLeastAncestor(theGraph, u, gp_GetVertexIndex(theGraph, u)); - - // Push edges to all unvisited neighbors. These will be either - // tree edges to children or forward arcs of back edges - // Edges not pushed are marked as back edges here, except the - // edge leading back to the immediate DFS parent. - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (!gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - { - sp_Push2(theStack, u, e); - } - else if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_PARENT) - { - // (2) Set the edge type values for back edges - gp_SetEdgeType(theGraph, e, EDGE_TYPE_BACK); - eTwin = gp_GetTwinArc(theGraph, e); - gp_SetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD); - - // (4) Move the twin of back edge record e to the sortedFwdArcList of the ancestor - uneighbor = gp_GetNeighbor(theGraph, e); - ePrev = gp_GetPrevArc(theGraph, eTwin); - eNext = gp_GetNextArc(theGraph, eTwin); - - if (gp_IsArc(ePrev)) - gp_SetNextArc(theGraph, ePrev, eNext); - else gp_SetFirstArc(theGraph, uneighbor, eNext); - if (gp_IsArc(eNext)) - gp_SetPrevArc(theGraph, eNext, ePrev); - else gp_SetLastArc(theGraph, uneighbor, ePrev); - - if (gp_IsArc(f = gp_GetVertexFwdArcList(theGraph, uneighbor))) - { - ePrev = gp_GetPrevArc(theGraph, f); - gp_SetPrevArc(theGraph, eTwin, ePrev); - gp_SetNextArc(theGraph, eTwin, f); - gp_SetPrevArc(theGraph, f, eTwin); - gp_SetNextArc(theGraph, ePrev, eTwin); - } - else - { - gp_SetVertexFwdArcList(theGraph, uneighbor, eTwin); - gp_SetPrevArc(theGraph, eTwin, eTwin); - gp_SetNextArc(theGraph, eTwin, eTwin); - } - - // (5) Update the leastAncestor value for the vertex u - uneighbor = gp_GetVertexIndex(theGraph, uneighbor); - if (uneighbor < gp_GetVertexLeastAncestor(theGraph, u)) - gp_SetVertexLeastAncestor(theGraph, u, uneighbor); - } - - e = gp_GetNextArc(theGraph, e); - } - } - } - } - - // The graph is now DFS numbered - theGraph->internalFlags |= FLAGS_DFSNUMBERED; - - // (6) Now that all vertices have a DFI in the index member, we can sort vertices - if (gp_SortVertices(theGraph) != OK) - return NOTOK; - - // Loop through the vertices and virtual vertices to... - for (v = gp_GetLastVertex(theGraph); gp_VertexInRangeDescending(theGraph, v); v--) - { - // (7) Initialize for pertinence management - gp_SetVertexVisitedInfo(theGraph, v, theGraph->N); - - // (7) Initialize for future pertinence management - child = gp_GetVertexSortedDFSChildList(theGraph, v); - gp_SetVertexFuturePertinentChild(theGraph, v, child); - leastValue = gp_GetVertexLeastAncestor(theGraph, v); - while (gp_IsVertex(child)) - { - if (leastValue > gp_GetVertexLowpoint(theGraph, child)) - leastValue = gp_GetVertexLowpoint(theGraph, child); - - child = gp_GetVertexNextDFSChild(theGraph, v, child); - } - gp_SetVertexLowpoint(theGraph, v, leastValue); - - // (8) Create the DFS tree embedding using the child edge records stored in the virtual vertices - // For each vertex v that is a DFS child, the virtual vertex R that will represent v's parent - // in the singleton bicomp with v is at location v + N in the vertex array. - if (gp_IsDFSTreeRoot(theGraph, v)) - { - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - } - else - { - R = gp_GetRootFromDFSChild(theGraph, v); - - // Make the child edge the only edge in the virtual vertex adjacency list - e = gp_GetFirstArc(theGraph, R); - gp_SetPrevArc(theGraph, e, NIL); - gp_SetNextArc(theGraph, e, NIL); - - // Reset the twin's neighbor value to point to the virtual vertex - eTwin = gp_GetTwinArc(theGraph, e); - gp_SetNeighbor(theGraph, eTwin, R); - - // Make its twin the only edge in the child's adjacency list - gp_SetFirstArc(theGraph, v, eTwin); - gp_SetLastArc(theGraph, v, eTwin); - gp_SetPrevArc(theGraph, eTwin, NIL); - gp_SetNextArc(theGraph, eTwin, NIL); - - // Set up the external face management data structure to match - gp_SetExtFaceVertex(theGraph, R, 0, v); - gp_SetExtFaceVertex(theGraph, R, 1, v); - gp_SetExtFaceVertex(theGraph, v, 0, R); - gp_SetExtFaceVertex(theGraph, v, 1, R); - } - } - - gp_LogLine("graphEmbed.c/_EmbeddingInitialize() end\n"); - -#ifdef PROFILE -platform_GetTime(end); -printf("Initialize embedding in %.3lf seconds.\n", platform_GetDuration(start,end)); -#endif - - return OK; -} - -/******************************************************************** - _EmbedBackEdgeToDescendant() - The Walkdown has found a descendant vertex W to which it can - attach a back edge up to the root of the bicomp it is processing. - The RootSide and WPrevLink indicate the parts of the external face - that will be replaced at each endpoint of the back edge. - ********************************************************************/ - -void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink) -{ -int fwdArc, backArc, parentCopy; - - /* We get the two edge records of the back edge to embed. - The Walkup recorded in W's adjacentTo the index of the forward arc - from the root's parent copy to the descendant W. */ - - fwdArc = gp_GetVertexPertinentEdge(theGraph, W); - backArc = gp_GetTwinArc(theGraph, fwdArc); - - /* The forward arc is removed from the fwdArcList of the root's parent copy. */ - - parentCopy = gp_GetPrimaryVertexFromRoot(theGraph, RootVertex); - - gp_LogLine(gp_MakeLogStr5("graphEmbed.c/_EmbedBackEdgeToDescendant() V=%d, R=%d, R_out=%d, W=%d, W_in=%d", - parentCopy, RootVertex, RootSide, W, WPrevLink)); - - if (gp_GetVertexFwdArcList(theGraph, parentCopy) == fwdArc) - { - gp_SetVertexFwdArcList(theGraph, parentCopy, gp_GetNextArc(theGraph, fwdArc)); - if (gp_GetVertexFwdArcList(theGraph, parentCopy) == fwdArc) - gp_SetVertexFwdArcList(theGraph, parentCopy, NIL); - } - - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc)); - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc)); - - // The forward arc is added to the adjacency list of the RootVertex. - // Note that we're guaranteed that the RootVertex adjacency list is non-empty, - // so tests for NIL are not needed - gp_SetAdjacentArc(theGraph, fwdArc, 1^RootSide, NIL); - gp_SetAdjacentArc(theGraph, fwdArc, RootSide, gp_GetArc(theGraph, RootVertex, RootSide)); - gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, RootVertex, RootSide), 1^RootSide, fwdArc); - gp_SetArc(theGraph, RootVertex, RootSide, fwdArc); - - // The back arc is added to the adjacency list of W. - // The adjacency list of W is also guaranteed non-empty - gp_SetAdjacentArc(theGraph, backArc, 1^WPrevLink, NIL); - gp_SetAdjacentArc(theGraph, backArc, WPrevLink, gp_GetArc(theGraph, W, WPrevLink)); - gp_SetAdjacentArc(theGraph, gp_GetArc(theGraph, W, WPrevLink), 1^WPrevLink, backArc); - gp_SetArc(theGraph, W, WPrevLink, backArc); - - gp_SetNeighbor(theGraph, backArc, RootVertex); - - /* Link the two endpoint vertices together on the external face */ - - gp_SetExtFaceVertex(theGraph, RootVertex, RootSide, W); - gp_SetExtFaceVertex(theGraph, W, WPrevLink, RootVertex); -} - -/******************************************************************** - _InvertVertex() - This function flips the orientation of a single vertex such that - instead of using link successors to go clockwise (or counterclockwise) - around a vertex's adjacency list, link predecessors would be used. - ********************************************************************/ - -void _InvertVertex(graphP theGraph, int W) -{ -int e, temp; - - gp_LogLine(gp_MakeLogStr1("graphEmbed.c/_InvertVertex() W=%d", W)); - - // Swap the links in all the arcs of the adjacency list - e = gp_GetFirstArc(theGraph, W); - while (gp_IsArc(e)) - { - temp = gp_GetNextArc(theGraph, e); - gp_SetNextArc(theGraph, e, gp_GetPrevArc(theGraph, e)); - gp_SetPrevArc(theGraph, e, temp); - - e = temp; - } - - // Swap the first/last edge record indicators in the vertex - temp = gp_GetFirstArc(theGraph, W); - gp_SetFirstArc(theGraph, W, gp_GetLastArc(theGraph, W)); - gp_SetLastArc(theGraph, W, temp); - - // Swap the first/last external face indicators in the vertex - temp = gp_GetExtFaceVertex(theGraph, W, 0); - gp_SetExtFaceVertex(theGraph, W, 0, gp_GetExtFaceVertex(theGraph, W, 1)); - gp_SetExtFaceVertex(theGraph, W, 1, temp); -} - -/******************************************************************** - _MergeVertex() - The merge step joins the vertex W to the root R of a child bicompRoot, - which is a root copy of W appearing in the region N to 2N-1. - - Actually, the first step of this is to redirect all of the edges leading - into R so that they indicate W as the neighbor instead of R. - For each edge node pointing to R, we set the 'v' field to W. Once an - edge is redirected from a root copy R to a parent copy W, the edge is - never redirected again, so we associate the cost of the redirection - as constant per edge, which maintains linear time performance. - - After this is done, a regular circular list union occurs. The only - consideration is that WPrevLink is used to indicate the two edge - records e_w and e_r that will become consecutive in the resulting - adjacency list of W. We set e_w to W's link [WPrevLink] and e_r to - R's link [1^WPrevLink] so that e_w and e_r indicate W and R with - opposing links, which become free to be cross-linked. Finally, - the edge record e_ext, set equal to R's link [WPrevLink], is the edge - that, with e_r, held R to the external face. Now, e_ext will be the - new link [WPrevLink] edge record for W. If e_w and e_r become part - of a proper face, then e_ext and W's link [1^WPrevLink] are the two - edges that attach W to the external face cycle of the containing bicomp. - ********************************************************************/ - -void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R) -{ -int e, eTwin, e_w, e_r, e_ext; - - gp_LogLine(gp_MakeLogStr4("graphEmbed.c/_MergeVertex() W=%d, W_in=%d, R=%d, R_out=%d", - W, WPrevLink, R, 1^WPrevLink)); - - // All arcs leading into R from its neighbors must be changed - // to say that they are leading into W - e = gp_GetFirstArc(theGraph, R); - while (gp_IsArc(e)) - { - eTwin = gp_GetTwinArc(theGraph, e); - gp_GetNeighbor(theGraph, eTwin) = W; - - e = gp_GetNextArc(theGraph, e); - } - - // Obtain the edge records involved in the list union - e_w = gp_GetArc(theGraph, W, WPrevLink); - e_r = gp_GetArc(theGraph, R, 1^WPrevLink); - e_ext = gp_GetArc(theGraph, R, WPrevLink); - - // If W has any edges, then join the list with that of R - if (gp_IsArc(e_w)) - { - // The WPrevLink arc of W is e_w, so the 1^WPrevLink arc in e_w leads back to W. - // Now it must lead to e_r. Likewise, e_r needs to lead back to e_w with the - // opposing link, which is WPrevLink - // Note that the adjacency lists of W and R are guaranteed non-empty, which is - // why these linkages can be made without NIL tests. - gp_SetAdjacentArc(theGraph, e_w, 1^WPrevLink, e_r); - gp_SetAdjacentArc(theGraph, e_r, WPrevLink, e_w); - - // Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext - gp_SetArc(theGraph, W, WPrevLink, e_ext); - gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, NIL); - } - // Otherwise, W just receives R's list. This happens, for example, on a - // DFS tree root vertex during JoinBicomps() - else - { - // Cross-link W's 1^WPrevLink arc and the WPrevLink arc in e_r - gp_SetArc(theGraph, W, 1^WPrevLink, e_r); - gp_SetAdjacentArc(theGraph, e_r, WPrevLink, NIL); - - // Cross-link W's WPrevLink arc and the 1^WPrevLink arc in e_ext - gp_SetArc(theGraph, W, WPrevLink, e_ext); - gp_SetAdjacentArc(theGraph, e_ext, 1^WPrevLink, NIL); - } - - // Erase the entries in R, which is a root copy that is no longer needed - _InitVertexRec(theGraph, R); -} - -/******************************************************************** - _MergeBicomps() - - Merges all biconnected components at the cut vertices indicated by - entries on the stack. - - theGraph contains the stack of bicomp roots and cut vertices to merge - - v, RootVertex, W and WPrevLink are not used in this routine, but are - used by overload extensions - - Returns OK, but an extension function may return a value other than - OK in order to cause Walkdown to terminate immediately. -********************************************************************/ - -int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink) -{ -int R, Rout, Z, ZPrevLink, e, extFaceVertex; - - while (sp_NonEmpty(theGraph->theStack)) - { - sp_Pop2(theGraph->theStack, R, Rout); - sp_Pop2(theGraph->theStack, Z, ZPrevLink); - - /* The external faces of the bicomps containing R and Z will - form two corners at Z. One corner will become part of the - internal face formed by adding the new back edge. The other - corner will be the new external face corner at Z. - We first want to update the links at Z to reflect this. */ - - extFaceVertex = gp_GetExtFaceVertex(theGraph, R, 1^Rout); - gp_SetExtFaceVertex(theGraph, Z, ZPrevLink, extFaceVertex); - - if (gp_GetExtFaceVertex(theGraph, extFaceVertex, 0) == gp_GetExtFaceVertex(theGraph, extFaceVertex, 1)) - // When (R, extFaceVertex) form a singleton bicomp, they have the same orientation, so the Rout link in extFaceVertex - // is the one that has to now point back to Z - gp_SetExtFaceVertex(theGraph, extFaceVertex, Rout, Z); - else - // When R and extFaceVertex are not alone in the bicomp, then they may not have the same orientation, so the - // ext face link that should point to Z is whichever one pointed to R, since R is a root copy of Z. - gp_SetExtFaceVertex(theGraph, extFaceVertex, gp_GetExtFaceVertex(theGraph, extFaceVertex, 0) == R ? 0 : 1, Z); - - /* If the path used to enter Z is opposed to the path - used to exit R, then we have to flip the bicomp - rooted at R, which we signify by inverting R - then setting the sign on its DFS child edge to - indicate that its descendants must be flipped later */ - - if (ZPrevLink == Rout) - { - Rout = 1^ZPrevLink; - - if (gp_GetFirstArc(theGraph, R) != gp_GetLastArc(theGraph, R)) - _InvertVertex(theGraph, R); - - e = gp_GetFirstArc(theGraph, R); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - // The core planarity algorithm could simply "set" the inverted flag - // because a bicomp root edge cannot be already inverted in the core - // planarity algorithm at the time of this merge. - // However, extensions may perform edge reductions on tree edges, resulting - // in an inversion sign being promoted to the root edge of a bicomp before - // it gets merged. So, xor is used to reverse the inversion flag on the - // root edge if the bicomp root must be inverted before it is merged. - gp_XorEdgeFlagInverted(theGraph, e); - break; - } - - e = gp_GetNextArc(theGraph, e); - } - } - - // R is no longer pertinent to Z since we are about to merge R into Z, so we delete R - // from Z's pertinent bicomp list (Walkdown gets R from the head of the list). - gp_DeleteVertexPertinentRoot(theGraph, Z, R); - - // If the merge will place the current future pertinence child into the same bicomp as Z, - // then we advance to the next child (or NIL) because future pertinence is - if (gp_GetDFSChildFromRoot(theGraph, R) == gp_GetVertexFuturePertinentChild(theGraph, Z)) - { - gp_SetVertexFuturePertinentChild(theGraph, Z, - gp_GetVertexNextDFSChild(theGraph, Z, gp_GetVertexFuturePertinentChild(theGraph, Z))); - } - - // Now we push R into Z, eliminating R - theGraph->functions.fpMergeVertex(theGraph, Z, ZPrevLink, R); - } - - return OK; -} - -/******************************************************************** - _WalkUp() - v is the vertex currently being embedded - e is the forward arc of the back edge to a descendant W of v - - The Walkup establishes pertinence for step v. It marks W with e - as a way of indicating it is pertinent because it should be made - 'adjacent to' v by adding a back edge (v', W), which will occur when - the Walkdown encounters W. - - The Walkup also determines the pertinent child bicomps that should be - set up as a result of the need to embed edge (v, W). It does this by - recording the pertinent child biconnected components of all cut - vertices between W and the child of v that is an ancestor of W. - Note that it stops the traversal if it finds a visited info value set - to v, which indicates that a prior walkup call in step v has already - done the work. This ensures work is not duplicated. - - A second technique used to maintain a total linear time bound for the - whole planarity method is that of parallel external face traversal. - This ensures that the cost of determining pertinence in step v is - linearly commensurate with the length of the path that ultimately - is removed from the external face. - - Zig and Zag are so named because one goes around one side of a bicomp - and the other goes around the other side, yet we have as yet no notion - of orientation for the bicomp. The edge record e from vertex v gestures - to a descendant vertex W in some other bicomp. Zig and Zag start out - at W. They go around alternate sides of the bicomp until its root is - found. We then hop from the root copy to the parent copy of the vertex - in order to record which bicomp we just came from and also to continue - the walk-up at the parent copy as if it were the new W. We reiterate - this process until the parent copy actually is v, at which point the - Walkup is done. - ********************************************************************/ - -void _WalkUp(graphP theGraph, int v, int e) -{ -int W = gp_GetNeighbor(theGraph, e); -int Zig=W, Zag=W, ZigPrevLink=1, ZagPrevLink=0; -int nextZig, nextZag, R; - - // Start by marking W as being directly pertinent - gp_SetVertexPertinentEdge(theGraph, W, e); - - // Zig and Zag are initialized at W, and we continue looping around - // the external faces of bicomps up from W until we reach vertex v - // (or until the visited info optimization breaks the loop) - while (Zig != v) - { - // Obtain the next vertex in a first direction and determine if it is a bicomp root - if (gp_IsVirtualVertex(theGraph, (nextZig = gp_GetExtFaceVertex(theGraph, Zig, 1^ZigPrevLink)))) - { - // If the current vertex along the external face was visited in this step v, - // then the bicomp root and its ancestor roots have already been added. - if (gp_GetVertexVisitedInfo(theGraph, Zig) == v) break; - - // Store the bicomp root that was found - R = nextZig; - - // Since the bicomp root was the next vertex on the path from Zig, determine the - // vertex on the opposing path that enters the bicomp root. - nextZag = gp_GetExtFaceVertex(theGraph, R, - gp_GetExtFaceVertex(theGraph, R, 0)==Zig ? 1 : 0); - - // If the opposing vertex was already marked visited in this step, then a prior - // Walkup already recorded as pertinent the bicomp root and its ancestor roots. - if (gp_GetVertexVisitedInfo(theGraph, nextZag) == v) break; - } - - // Obtain the next vertex in the parallel direction and perform the analogous logic - else if (gp_IsVirtualVertex(theGraph, (nextZag = gp_GetExtFaceVertex(theGraph, Zag, 1^ZagPrevLink)))) - { - if (gp_GetVertexVisitedInfo(theGraph, Zag) == v) break; - R = nextZag; - nextZig = gp_GetExtFaceVertex(theGraph, R, - gp_GetExtFaceVertex(theGraph, R, 0)==Zag ? 1 : 0); - if (gp_GetVertexVisitedInfo(theGraph, nextZig) == v) break; - } - - // The bicomp root was not found in either direction. - else - { - if (gp_GetVertexVisitedInfo(theGraph, Zig) == v) break; - if (gp_GetVertexVisitedInfo(theGraph, Zag) == v) break; - R = NIL; - } - - // This Walkup has now finished with another vertex along each of the parallel - // paths, so they are marked visited in step v so that future Walkups in this - // step v can break if these vertices are encountered again. - gp_SetVertexVisitedInfo(theGraph, Zig, v); - gp_SetVertexVisitedInfo(theGraph, Zag, v); - - // If both directions found new non-root vertices, then proceed with parallel external face traversal - if (gp_IsNotVertex(R)) - { - ZigPrevLink = gp_GetExtFaceVertex(theGraph, nextZig, 0)==Zig ? 0 : 1; - Zig = nextZig; - - ZagPrevLink = gp_GetExtFaceVertex(theGraph, nextZag, 0)==Zag ? 0 : 1; - Zag = nextZag; - } - - // The bicomp root was found and not previously recorded as pertinent, - // so walk up to the parent bicomp and continue - else - { - // Step up from the root (virtual) vertex to the primary (non-virtual) vertex - Zig = Zag = gp_GetPrimaryVertexFromRoot(theGraph, R); - ZigPrevLink = 1; - ZagPrevLink = 0; - - // Add the new root vertex to the list of pertinent bicomp roots of the primary vertex. - // The new root vertex is appended if future pertinent and prepended if only pertinent - // so that, by virtue of storage, the Walkdown will process all pertinent bicomps that - // are not future pertinent before any future pertinent bicomps. - - // NOTE: Unlike vertices, the activity status of a bicomp is computed solely using - // the lowpoint of the DFS child in the bicomp's root edge, which indicates - // whether the DFS child or any of its descendants connect by a back edge to - // ancestors of v. If so, then the bicomp rooted at RootVertex must contain a - // future pertinent vertex that must be kept on the external face. - if (gp_GetVertexLowpoint(theGraph, gp_GetDFSChildFromRoot(theGraph, R)) < v) - gp_AppendVertexPertinentRoot(theGraph, Zig, R); - else gp_PrependVertexPertinentRoot(theGraph, Zag, R); - } - } -} - -/******************************************************************** - _WalkDown() - Consider a circular shape with small circles and squares along its perimeter. - The small circle at the top is the root vertex of the bicomp. The other small - circles represent active vertices, and the squares represent future pertinent - vertices. The root vertex is a root copy of v, the vertex currently being processed. - - The Walkup previously marked all vertices adjacent to v by setting their - pertinentEdge members with the forward arcs of the back edges to embed. - Two Walkdown traversals are performed to visit all reachable vertices - along each of the external face paths emanating from RootVertex (a root - copy of vertex v) to embed back edges to descendants of vertex v that - have their pertinentEdge members marked. - - During each Walkdown traversal, it is sometimes necessary to hop from a - vertex to one of its child biconnected components in order to reach the - desired vertices. In such cases, the biconnected components are merged - such that adding the back edge forms a new proper face in the biconnected - component rooted at RootVertex (which, again, is a root copy of v). - - The outer loop performs both walks, unless the first walk got all the way - around to RootVertex (only happens when bicomp contains no external activity, - such as when processing the last vertex), or when non-planarity is - discovered (in a pertinent child bicomp such that the stack is non-empty). - - For the inner loop, each iteration visits a vertex W. If W is marked as - requiring a back edge, then MergeBicomps is called to merge the biconnected - components whose cut vertices have been collecting in merge stack. Then, - the back edge (RootVertex, W) is added, and the pertinentEdge of W is cleared. - - Next, we check whether W has a pertinent child bicomp. If so, then we figure - out which path down from the root of the child bicomp leads to the next vertex - to be visited, and we push onto the stack information on the cut vertex and - the paths used to enter into it and exit from it. Alternately, if W - had no pertinent child bicomps, then we check to see if it is inactive. - If so, we find the next vertex along the external face, then short-circuit - its inactive predecessor (under certain conditions). Finally, if W is not - inactive, but it has no pertinent child bicomps, then we already know its - adjacentTo flag is clear so both criteria for internal activity also fail. - Therefore, W must be a stopping vertex. - - A stopping vertex X is a future pertinent vertex that has no pertinent - child bicomps and no unembedded back edge to the current vertex v. - The inner loop of Walkdown stops walking when it reaches a stopping vertex X - because if it were to proceed beyond X and embed a back edge, then X would be - surrounded by the bounding cycle of the bicomp. This would clearly be - incorrect because X has a path leading from it to an ancestor of v, which - would have to cross the bounding cycle. - - Either Walkdown traversal can halt the Walkdown and return if a pertinent - child biconnected component to which the traversal has descended is blocked, - i.e. has stopping vertices on both paths emanating from the root. This - indicates an obstruction to embedding. In core planarity it is evidence of - a K_{3,3}, but some extension algorithms are able to clear the blockage and - proceed with embedding. - - If both Walkdown traversals successfully completed, then the outer loop - ends. Post-processing code tests whether the Walkdown embedded all the - back edges from v to its descendants in the subtree rooted by c, a DFS - child of v uniquely associated with the RootVertex. If not, then embedding - was obstructed. In core planarity it is evidence of a K_{3,3} or K_5, but some - extension algorithms are able to clear the blockage and proceed with embedding. - - Returns OK if all possible edges were embedded, - NONEMBEDDABLE if less than all possible edges were embedded, - NOTOK for an internal code failure - ********************************************************************/ - -int _WalkDown(graphP theGraph, int v, int RootVertex) -{ -int RetVal, W, WPrevLink, R, X, XPrevLink, Y, YPrevLink, RootSide, e; -int RootEdgeChild = gp_GetDFSChildFromRoot(theGraph, RootVertex); - - sp_ClearStack(theGraph->theStack); - - for (RootSide = 0; RootSide < 2; RootSide++) - { - W = gp_GetExtFaceVertex(theGraph, RootVertex, RootSide); - - // Determine the link used to enter W based on which side points back to RootVertex - // Implicitly handled special case: In core planarity, the first Walkdown traversal - // Will be on a singleton edge. In this case, RootVertex and W are *consistently* - // oriented, and the RootSide is 0, so WPrevLink should be 1. This calculation is - // written to implicitly produce that result. - WPrevLink = gp_GetExtFaceVertex(theGraph, W, 1) == RootVertex ? 1 : 0; - - while (W != RootVertex) - { - // Detect unembedded back edge descendant endpoint W - if (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, W))) - { - // Merge any bicomps whose cut vertices were traversed to reach W, then add the - // edge to W to form a new proper face in the embedding. - if (sp_NonEmpty(theGraph->theStack)) - { - if ((RetVal = theGraph->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink)) != OK) - return RetVal; - } - theGraph->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink); - - // Clear W's pertinentEdge since the forward arc it contained has been embedded - gp_SetVertexPertinentEdge(theGraph, W, NIL); - } - - // If W has a pertinent child bicomp, then we descend to the first one... - if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) - { - // Push the vertex W and the direction of entry, then descend to a root copy R of W - sp_Push2(theGraph->theStack, W, WPrevLink); - R = gp_GetVertexFirstPertinentRoot(theGraph, W); - - // Get the next active vertices X and Y on the external face paths emanating from R - X = gp_GetExtFaceVertex(theGraph, R, 0); - XPrevLink = gp_GetExtFaceVertex(theGraph, X, 1)==R ? 1 : 0; - Y = gp_GetExtFaceVertex(theGraph, R, 1); - YPrevLink = gp_GetExtFaceVertex(theGraph, Y, 0)==R ? 0 : 1; - - // Now we implement the Walkdown's simple path selection rules! - // Select a direction from the root to a pertinent vertex, - // preferentially toward a vertex that is not future pertinent - gp_UpdateVertexFuturePertinentChild(theGraph, X, v); - gp_UpdateVertexFuturePertinentChild(theGraph, Y, v); - if (PERTINENT(theGraph, X) && NOTFUTUREPERTINENT(theGraph, X, v)) - { - W = X; - WPrevLink = XPrevLink; - sp_Push2(theGraph->theStack, R, 0); - } - else if (PERTINENT(theGraph, Y) && NOTFUTUREPERTINENT(theGraph, Y, v)) - { - W = Y; - WPrevLink = YPrevLink; - sp_Push2(theGraph->theStack, R, 1); - } - else if (PERTINENT(theGraph, X)) - { - W = X; - WPrevLink = XPrevLink; - sp_Push2(theGraph->theStack, R, 0); - } - else if (PERTINENT(theGraph, Y)) - { - W = Y; - WPrevLink = YPrevLink; - sp_Push2(theGraph->theStack, R, 1); - } - else - { - // Both the X and Y sides of the descendant bicomp are blocked. - // Let the application decide whether it can unblock the bicomp. - // The core planarity/outerplanarity embedder simply isolates a - // planarity/outerplanary obstruction and returns NONEMBEDDABLE - if ((RetVal = theGraph->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R)) != OK) - return RetVal; - - // If an extension algorithm cleared the blockage, then we pop W and WPrevLink - // back off the stack and let the Walkdown traversal try descending again - sp_Pop2(theGraph->theStack, W, WPrevLink); - } - } - else - { - // The vertex W is known to be non-pertinent, so if it is future pertinent - // (or if the algorithm is based on outerplanarity), then the vertex is - // a stopping vertex for the Walkdown traversal. - gp_UpdateVertexFuturePertinentChild(theGraph, W, v); - if (FUTUREPERTINENT(theGraph, W, v) || (theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR)) - { - // Create an external face short-circuit between RootVertex and the stopping vertex W - // so that future steps do not walk down a long path of inactive vertices between them. - // As a special case, we ensure that the external face is not reduced to just two - // vertices, W and RootVertex, because it would then become a challenge to determine - // whether W has the same orientation as RootVertex. - // So, if the other side of RootVertex is already attached to W, then we simply push - // W back one vertex so that the external face will have at least three vertices. - if (gp_GetExtFaceVertex(theGraph, RootVertex, 1^RootSide) == W) - { - X = W; - W = gp_GetExtFaceVertex(theGraph, W, WPrevLink); - WPrevLink = gp_GetExtFaceVertex(theGraph, W, 0) == X ? 1 : 0; - } - gp_SetExtFaceVertex(theGraph, RootVertex, RootSide, W); - gp_SetExtFaceVertex(theGraph, W, WPrevLink, RootVertex); - - // Terminate the Walkdown traversal since it encountered the stopping vertex - break; - } - - // If the vertex is neither pertinent nor future pertinent, then it is inactive. - // The default handler planarity handler simply skips inactive vertices by traversing - // to the next vertex on the external face. - // Once upon a time, false edges called short-circuit edges were added to eliminate - // inactive vertices, but the extFace links above achieve the same result with less work. - else - { - if (theGraph->functions.fpHandleInactiveVertex(theGraph, RootVertex, &W, &WPrevLink) != OK) - return NOTOK; - } - } - } - } - - // Detect and handle the case in which Walkdown was blocked from embedding all the back edges from v - // to descendants in the subtree of the child of v associated with the bicomp RootVertex. - if (gp_IsArc(e = gp_GetVertexFwdArcList(theGraph, v)) && RootEdgeChild < gp_GetNeighbor(theGraph, e)) - { - int nextChild = gp_GetVertexNextDFSChild(theGraph, v, RootEdgeChild); - - // The Walkdown was blocked from embedding all forward arcs into the RootEdgeChild subtree - // if there the next child's DFI is greater than the descendant endpoint of the next forward arc, - // or if there is no next child. - if (gp_IsNotVertex(nextChild) || nextChild > gp_GetNeighbor(theGraph, e)) - { - // If an extension indicates it is OK to proceed despite the unembedded forward arcs, then - // advance to the forward arcs for the next child, if any - if ((RetVal = theGraph->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, RootVertex)) == OK) - _AdvanceFwdArcList(theGraph, v, RootEdgeChild, nextChild); - - return RetVal; - } - } - - return OK; -} - -/******************************************************************** - _HandleBlockedBicomp() - - A biconnected component has blocked the Walkdown from embedding - back edges. Each external face path emanating from the root is - blocked by a stopping vertex. - - The core planarity/outerplanarity algorithm handles the blockage - by isolating an embedding obstruction (a subgraph homeomorphic to - K_{3,3} or K_5 for planarity, or a subgraph homeomorphic to K_{2,3} - or K_4 for outerplanarity). Then NONEMBEDDABLE is returned so that - the WalkDown can terminate. - - Extension algorithms are able to clear some of the blockages, in - which case OK is returned to indicate that the WalkDown can proceed. - - Returns OK to proceed with WalkDown at W, - NONEMBEDDABLE to terminate WalkDown of Root Vertex - NOTOK for internal error - ********************************************************************/ - -int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) -{ - int RetVal = NONEMBEDDABLE; - - if (R != RootVertex) - sp_Push2(theGraph->theStack, R, 0); - - if (theGraph->embedFlags == EMBEDFLAGS_PLANAR) - { - if (_IsolateKuratowskiSubgraph(theGraph, v, RootVertex) != OK) - RetVal = NOTOK; - } - else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR) - { - if (_IsolateOuterplanarObstruction(theGraph, v, RootVertex) != OK) - RetVal = NOTOK; - } - - return RetVal; -} - -/******************************************************************** - _AdvanceFwdArcList() - - If an extension determines that it is OK to leave some forward arcs - unembedded, then we advance the forward arc list head pointer past - the unembedded arcs for the current child so that it points to the - first forward arc for the next child, if any. - - There are two meanings of the phrase "if any". First, there may be - no next child, in which case nextChild is NIL, and the forward arc - list need not be advanced. - - If there is a next child, then the forward arc list head needs to - be advanced to the first arc whose descendant endpoint is greater - than the nextChild, if any. However, the tail end of the forward arc - list may include unembedded forward arcs to a preceding sibling - of the child vertex. So, we advance an edge pointer e until one of - the following happens: - - 1) e gets all the way around to the forward arc list head - 2) e finds an edge whose descendant endpoint is less than the child - 3) e finds an edge whose descendant endpoint is greater than the next child - - In case 1, all the forward arcs belong in the subtree of the child, so - there is no need to change the forward arc list head. - - In case 2, there are no more forward arcs to any following siblings of - the child, only left-behind unembedded forward arcs that we advanced - past in previous calls to this method from Walkdowns of the preceding - children of v. So the forward arc list head should be set to e so that - it is set to the forward arc with the least numbered descendant endpoint. - - In case 3, the desired forward arc into the subtree of a following sibling - of the child has been found, so again the forward arc list head should be - set to e to indicate that edge. - - After all Walkdowns of the children of a vertex, the forward arc list will - be NIL if all edges were embedded, or it will indicate the unembedded - forward arc whose descendant endpoint has the least number. Cases 1 and 2 - directly implement this in cases where a Walkdown for the given child - fails to embed an edge, and case 3 indirectly finishes the job by making - sure the forward arc list head has the right value at the beginning of - a Walkdown for a particular child. If the Walkdown of that child succeeds - at embedding all the forward edges into that child's subtree, then each - embedding advances the forward arc list head. So, even if the Walkdown - of the last pertinent child embeds all forward arcs, then the Walkdown - itself advances the forward arc list head to the first unembedded forward - arc, or to NIL. - ********************************************************************/ - -void _AdvanceFwdArcList(graphP theGraph, int v, int child, int nextChild) -{ - int e = gp_GetVertexFwdArcList(theGraph, v); - - while (gp_IsArc(e)) - { - // 2) e finds an edge whose descendant endpoint is less than the child - if (gp_GetNeighbor(theGraph, e) < child) - { - gp_SetVertexFwdArcList(theGraph, v, e); - break; - } - - // 3) e finds an edge whose descendant endpoint is greater than the next child - else if (gp_IsVertex(nextChild) && nextChild < gp_GetNeighbor(theGraph, e)) - { - gp_SetVertexFwdArcList(theGraph, v, e); - break; - } - - e = gp_GetNextArc(theGraph, e); - // 1) e gets all the way around to the forward arc list head - if (e == gp_GetVertexFwdArcList(theGraph, v)) - e = NIL; - } -} - -/******************************************************************** - _HandleInactiveVertex() - - Although it is possible to short-circuit every inactive vertex from - the external face, for efficiency the Walkdown traversal now just does - a single short-circuit between the bicomp root and a stopping vertex. - This is because the main thing that short-circuiting needs to optimize - is the Walkdown's choice of direction after descending to the root - of a pertinent biconnected component. So, the Walkdown just optimizes - the external face of a biconnected component as it finishes processing - it so it will be ready in future steps when it becomes pertinent. - Hence, when traversing the face of a bicomp during the current step, - we only need to skip an inactive vertex and traverse to the next vertex - on the external face. - ********************************************************************/ - -int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink) -{ - int X = gp_GetExtFaceVertex(theGraph, *pW, 1^*pWPrevLink); - *pWPrevLink = gp_GetExtFaceVertex(theGraph, X, 0) == *pW ? 0 : 1; - *pW = X; - - return OK; -} - -/******************************************************************** - _EmbedPostprocess() - - After the loop that embeds the cycle edges from each vertex to its - DFS descendants, this method is invoked to postprocess the graph. - If the graph is planar or outerplanar, then a consistent orientation - is imposed on the vertices of the embedding, and any remaining - separated biconnected components are joined together. - If the graph is non-planar or non-outerplanar, then an obstruction - to planarity or outerplanarity has already been isolated. - Extensions may override this function to provide alternate behavior. - - @param theGraph - the graph ready for postprocessing - @param v - the last vertex processed by the edge embedding loop - @param edgeEmbeddingResult - - OK if all edge embedding iterations returned OK - NONEMBEDDABLE if an embedding iteration failed to embed - all edges for a vertex - - @return NOTOK on internal failure - NONEMBEDDABLE if a subgraph homeomorphic to a topological - obstruction is isolated in the graph - OK otherwise (e.g. if the graph contains an embedding) - *****************************************************************/ - -int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) -{ -int RetVal = edgeEmbeddingResult; - - // If an embedding was found, then post-process the embedding structure give - // a consistent orientation to all vertices then eliminate virtual vertices - if (edgeEmbeddingResult == OK) - { - if (_OrientVerticesInEmbedding(theGraph) != OK || - _JoinBicomps(theGraph) != OK) - RetVal = NOTOK; - } - - // If the graph is embedded (OK) or NONEMBEDDABLE, we pass the result back - return RetVal; -} - -/******************************************************************** - _OrientVerticesInEmbedding() - - Each vertex will then have an orientation, either clockwise or - counterclockwise. All vertices in each bicomp need to have the - same orientation. - This method clears the stack, and the stack is clear when it - is finished. - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _OrientVerticesInEmbedding(graphP theGraph) -{ -int R; - - sp_ClearStack(theGraph->theStack); - - // For each vertex, obtain the associated bicomp root location and, - // if it is still in use as a bicomp root, orient the vertices in the bicomp - for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) - { - if (gp_VirtualVertexInUse(theGraph, R)) - { - if (_OrientVerticesInBicomp(theGraph, R, 0) != OK) - return NOTOK; - } - } - return OK; -} - -/******************************************************************** - _OrientVerticesInBicomp() - As a result of the work done so far, the edges around each vertex have - been put in order, but the orientation may be counterclockwise or - clockwise for different vertices within the same bicomp. - We need to reverse the orientations of those vertices that are not - oriented the same way as the root of the bicomp. - - During embedding, a bicomp with root edge (v', c) may need to be flipped. - We do this by inverting the root copy v' and implicitly inverting the - orientation of the vertices in the subtree rooted by c by assigning -1 - to the sign of the DFSCHILD edge record leading to c. - - We now use these signs to help propagate a consistent vertex orientation - throughout all vertices that have been merged into the given bicomp. - The bicomp root contains the orientation to be imposed on all parent - copy vertices. We perform a standard depth first search to visit each - vertex. A vertex must be inverted if the product of the edge signs - along the tree edges between the bicomp root and the vertex is -1. - - Finally, the PreserveSigns flag, if set, performs the inversions - but does not change any of the edge signs. This allows a second - invocation of this function to restore the state of the bicomp - as it was before the first call. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most two integers per vertext in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns) -{ -int W, e, invertedFlag; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push2(theGraph->theStack, BicompRoot, 0); - - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - /* Pop a vertex to orient */ - sp_Pop2(theGraph->theStack, W, invertedFlag); - - /* Invert the vertex if the inverted flag is set */ - if (invertedFlag) - _InvertVertex(theGraph, W); - - /* Push the vertex's DFS children that are in the bicomp */ - e = gp_GetFirstArc(theGraph, W); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push2(theGraph->theStack, gp_GetNeighbor(theGraph, e), - invertedFlag ^ gp_GetEdgeFlagInverted(theGraph, e)); - - if (!PreserveSigns) - gp_ClearEdgeFlagInverted(theGraph, e); - } - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _JoinBicomps() - The embedding algorithm works by only joining bicomps once the result - forms a larger bicomp. However, if the original graph was separable - or disconnected, then the result of the embed function will be a - graph that contains each bicomp as a distinct entity. This function - merges the bicomps into one connected graph. - ********************************************************************/ - -int _JoinBicomps(graphP theGraph) -{ - int R; - - for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) - { - // If the root is still active (i.e. an in-use virtual vertex) - // then merge it with its primary (non-virtual) counterpart - if (gp_VirtualVertexInUse(theGraph, R)) - _MergeVertex(theGraph, gp_GetPrimaryVertexFromRoot(theGraph, R), 0, R); - } - - return OK; -} - -/**************************************************************************** - _OrientExternalFacePath() - - The vertices along the path (v ... w) are assumed to be degree two vertices - in an external face path connecting u and x. This method imparts the - orientation of u and x onto the vertices v ... w. - The work done is on the order of the path length. - Returns OK if the external face path was oriented, NOTOK on implementation - error (i.e. if a condition arises providing the path is not on the - external face). - ****************************************************************************/ - -int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x) -{ -int e_u, e_v, e_ulink, e_vlink; - - // Get the edge record in u that indicates v; uses the twinarc method to - // ensure the cost is dominated by the degree of v (which is 2), not u - // (which can be any degree). - e_u = gp_GetTwinArc(theGraph, gp_GetNeighborEdgeRecord(theGraph, v, u)); - - do { - // Get the external face link in vertex u that indicates the - // edge e_u which connects to the next vertex v in the path - // As a sanity check, we determine whether e_u is an - // external face edge, because there would be an internal - // implementation error if not - if (gp_GetFirstArc(theGraph, u) == e_u) - e_ulink = 0; - else if (gp_GetLastArc(theGraph, u) == e_u) - e_ulink = 1; - else return NOTOK; - - v = gp_GetNeighbor(theGraph, e_u); - - // Now get the external face link in vertex v that indicates the - // edge e_v which connects back to the prior vertex u. - e_v = gp_GetTwinArc(theGraph, e_u); - - if (gp_GetFirstArc(theGraph, v) == e_v) - e_vlink = 0; - else if (gp_GetLastArc(theGraph, v) == e_v) - e_vlink = 1; - else return NOTOK; - - // The vertices u and v are inversely oriented if they - // use the same link to indicate the edge [e_u, e_v]. - if (e_vlink == e_ulink) - { - _InvertVertex(theGraph, v); - e_vlink = 1^e_vlink; - } - - // This update of the extFace short-circuit is polite but unnecessary. - // This orientation only occurs once we know we can isolate a K_{3,3}, - // at which point the extFace data structure is not used. - gp_SetExtFaceVertex(theGraph, u, e_ulink, v); - gp_SetExtFaceVertex(theGraph, v, e_vlink, u); - - u = v; - e_u = gp_GetArc(theGraph, v, 1^e_vlink); - } while (u != x); - - return OK; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.c deleted file mode 100644 index 199603bd8..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.c +++ /dev/null @@ -1,559 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include -#include - -#include "appconst.h" - -#include "graphExtensions.private.h" -#include "graphExtensions.h" -#include "graphFunctionTable.h" - -#ifdef __GNUC__ - #ifndef __clang__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wshadow" - #endif -#endif - -/* Imported functions */ - -extern void _InitFunctionTable(graphP theGraph); - -/* Private function */ - -void _FreeExtension(graphExtensionP extension); -void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions); -void _FixupFunctionTables(graphP theGraph, graphExtensionP curr); -graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex); - -/******************************************************************** - * The moduleIDGenerator is used to help ensure that all extensions - * added during a run-time have a different integer identifier. - * An ID identifies an extension, which may be added to multiple - * graphs. It is used in lieu of identifying extensions by a string - * name, which is noticeably expensive when a frequently called - * overload function seeks the extension context for a graph. - ********************************************************************/ - -static int moduleIDGenerator = 0; - -/******************************************************************** - The extension mechanism allows new modules to equip a graph with the - data structures and functions needed to implement new algorithms - without impeding the performance of the core graph planar embedding - algorithms on graphs that have not been so equipped. - - The following steps must be used to create a graph extension: - - 1) Create a moduleID variable initialized to zero that will be - assigned a positive integer the first time the extension is - added to a graph by gp_AddExtension() - - 2) Define an extension context structure to contain all of the data - and function pointers that extend the graph. The context must - include a graphFunctionTable to allow overloading of functions. - An instance of this context structure is passed to the "context" - parameter of gp_AddExtension(). - - 3) Define a function capable of duplicating your context data - structure. It receives a void pointer indicating the context - to duplicate and a void pointer that can be cast to a graph - pointer indicating the graph for which the context is being - duplicated. The void pointer returned indicates the newly - allocated context structure. The pointer to this function is - passed to the "dupContext" parameter of gp_AddExtension() - - Note: It is useful to store in your context structure a pointer - to the graph that the context is extending. There are certain - function overloads you will perform that will only receive - the context, and you may need to know things about the graph, - such as the number of vertices or edges. - - 4) Define a function that can free the memory used by your context - data structure. It will receive a void pointer indicating the - instance of your context data structure that you passed as the - "context" parameter to gp_AddExtension(). - The free function pointer should be passed as the "freeContext" - parameter to gp_AddExtension() - - 5) The expected method of attaching your feature to a graph is to - create a function called gp_AttachFeature(), where 'Feature' is - the name of your module. The attach function allocates your context - data structure, initializes the extension data, assigns overload - function pointers, and invokes gp_AddExtension(). - - NOTE: It is advisable to use memset on the context function table - before assigning any function overloads because any function not - being overloaded must have a NULL pointer. - - NOTE: The gp_AddExtension() method puts the overload function - pointers into the graph's function table, and the base function - pointers that were overloaded are placed in the context function - table. This allows the extension's functions to have access to - base function behaviors, since many extension functions will - extend rather than replace the base behavior. - - 6) There are a few functions that you must overload in order to - successfully manage data structures that are parallel to the - main graph data structures. - - The core graph data structure has function pointers to functions - that can be overloaded. In addition to invoking gp_AddExtension(), - you need to set pointers to your own versions of the functions - you are overloading. You will also need to store a copy of the - prior pointer in your feature's context data structure so that you - can invoke the "base" behavior from your function overload, e.g. - if your feature is attached but not active or if your feature - augments the base behavior rather than replacing it. - - a) If any kind of data structures needs to be maintained at - the graph, vertex or edge levels, then an overload of - fpInitGraph() will be needed. - - b) If any data must be associated with primary and virtual vertices, - then an overload of fpInitVertexRec() is needed. If data must be - associated only with primary vertices (0 to N-1), then one can - overload fpInitVertexInfo() instead. - The overload function should be named _Feature_InitVertexRec() - or _Feature_InitVertexInfo(). - It will invoke the base fpInitVertexRec() or fpInitVertexInfo() - but then also invoke a second function named _InitFeatureVertexRec() - or _InitFeatureVertexInfo() thatinitializes the custom VertexRec - or VertexInfo data members. - - c) If any data must be associated with the edges, then an overload - of fpInitEdgeRec() is needed. - This overload function should be named _Feature_InitEdgeRec(). - It will invoke the base fpInitEdgeRec() and also invoke - a second function named_InitFeatureEdgeRec() that - initializes the custom EdgeRec data members - - d) If any graph-level data structures are needed, then an - overload of fpReinitializeGraph() will also be needed, not just the - overload of fpInitGraph(). However, if only vertex-level and/or - edge level data members are needed, then the overloads of - fpInitVertexRec(), fpInitVertexInfo() and/or fpInitEdgeRec() are - invoked by the basic fpReinitializeGraph without needing to overload - it as well. - - e) If any data must be persisted in the file format, then overloads - of fpReadPostprocess() and fpWritePostprocess() are needed. - - 7) Define internal functions for _Feature_ClearStructures(), - _Feature_CreateStructures() and _Feature_InitStructures(); - - a) The _Feature_ClearStructures() should simply null out pointers - to extra structures on its first invocation, but thereafter it - should free them and then null them. Since the null-only step - is done only once in gp_AttachFeature(), it seems reasonable to - not bother with a more complicated _Feature_ClearStructures(). - But, as an extension is developed, the data structures change, - so it is best to keep all this logic in one place. - - b) The _Feature_CreateStructures() should just allocate memory for - but not initialize any vertex level and edge level data structures. - Data structures maintained at the graph level, such as a stack or a - list collection, should be created _and_ initialized. - - c) The _Feature_InitStructures() should invoke just the functions - needed to initialize the custom VertexRec, VertexInfo and EdgeRec - data members, if any. - - 8) Define a function gp_DetachFeature() that invokes gp_RemoveExtension() - This should be done for consistency, so that users of a feature - do not attach it with gp_AttachFeature() and remove it with - gp_RemoveExtension(). However, it may sometimes be necessary to - run more code than just gp_RemoveExtension() when detaching a feature, - e.g. some final result values of a feature may be saved to data - available in the core graph or in other features. - ********************************************************************/ - -/******************************************************************** - gp_AddExtension() - - @param theGraph - pointer to the graph to which the extension is being added - @param pModuleID - address of the variable that contains the feature's - extension identifier. If the variable is equal to zero, - it is assigned a positive number. Thereafter, the variable - value can be used to find and remove the extension from any graph - @param context - the data storage for the extension being added - The context is owned by the extension and freed with freeContext() - @param dupContext - a function capable of duplicating the context data - @param freeContext - a function capable of freeing the context data - @param functions - pointer to a table of functions stored in the data context. - The table of functions is an input and output parameter. - On input, the table consists of new function pointers - for functions being overloaded. - Any function not being overloaded must be NULL. - The non-NULL function pointers are used to overload - the functions in the graph, and the prior pointer values - in the graph are stored in the function table as output. - The context data therefore has the pointer to the base - function corresponding to any function its extension - module overloaded. - - The new extension is created and added to the graph. - ********************************************************************/ - -int gp_AddExtension(graphP theGraph, - int *pModuleID, - void *context, - void *(*dupContext)(void *, void *), - void (*freeContext)(void *), - graphFunctionTableP functions) -{ - graphExtensionP newExtension = NULL; - - if (theGraph == NULL || pModuleID == NULL || - context == NULL || dupContext == NULL || freeContext == NULL || - functions == NULL) - { - return NOTOK; - } - - // If the extension already exists, then don't redefine it. - if (gp_FindExtension(theGraph, *pModuleID, NULL) == TRUE) - { - return NOTOK; - } - - // Assign a unique ID to the extension if it does not already have one - if (*pModuleID == 0) - { - *pModuleID = ++moduleIDGenerator; - } - - // Allocate the new extension - if ((newExtension = (graphExtensionP) malloc(sizeof(graphExtension))) == NULL) - { - return NOTOK; - } - - // Assign the data payload of the extension - newExtension->moduleID = *pModuleID; - newExtension->context = context; - newExtension->dupContext = dupContext; - newExtension->freeContext = freeContext; - newExtension->functions = functions; - - _OverloadFunctions(theGraph, functions); - - // Make the new linkages - newExtension->next = (struct graphExtension *) theGraph->extensions; - theGraph->extensions = newExtension; - - // The new extension was successfully added - return OK; - -} - -/******************************************************************** - _OverloadFunctions() - For each non-NULL function pointer, the pointer becomes the new value - for the function in the graph, and the old function pointer in the graph - is placed in the overload table. - - This way, when an extension function is invoked, it can choose to invoke - the base function before or after whatever extension behavior it provides. - - Also, when it comes time to remove an extension, this extension system - has access to the overload tables of all extensions so that it can unhook - the functions of the module being removed from the chains of calls for - each overloaded function. This will involve some pointer changes in - the overload tables of extensions other than the one being removed. - ********************************************************************/ - -void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions) -{ -void **graphFunctionTable = (void **) &theGraph->functions; -void **newFunctionTable = (void **) functions; -int numFunctions = sizeof(theGraph->functions) / sizeof(void *); -int K; - - for (K = 0; K < numFunctions; K++) - { - if (newFunctionTable[K] != NULL) - { - void *fp = graphFunctionTable[K]; - graphFunctionTable[K] = newFunctionTable[K]; - newFunctionTable[K] = fp; - } - } -} - -/******************************************************************** - gp_FindExtension() - - @param theGraph - the graph whose extension list is to be searched - @param moduleID - the identifier of the module whose extension context is desired - @param pContext - the return parameter that receives the value of the - extension, if found. This may be NULL if the extension was - not found or if the extension context value was NULL. - @return TRUE if the extension was found, NOTOK if not found - If FALSE is returned, then the context returned is guaranteed to be NULL - If TRUE is returned, the context returned may be NULL if that is the - current value of the module extension - ********************************************************************/ - -int gp_FindExtension(graphP theGraph, int moduleID, void **pContext) -{ - graphExtensionP first = NULL, next = NULL; - - if (pContext != NULL) - { - *pContext = NULL; - } - - if (theGraph==NULL || moduleID==0) - { - return FALSE; - } - - first = theGraph->extensions; - - while (first != NULL) - { - next = (graphExtensionP) first->next; - if (first->moduleID == moduleID) - { - if (pContext != NULL) - { - *pContext = first->context; - } - return TRUE; - } - first = next; - } - - return FALSE; -} - -/******************************************************************** - gp_GetExtension() - - Calling this function is equivalent to invoking gp_FindExtension() - except that some debuggers have difficulty stepping into a function - that (properly) start by setting a local variable pointer to NULL - when the debugger has watch expressions that dereference a pointer - of the same name. In such cases, - - MyContext *context = NULL; - gp_FindExtension(theGraph, MYEXTENSION_ID, &context); - - can be replaced by - - MyContext *context = gp_GetExtension(theGraph, MYEXTENSION_ID); - - @param theGraph - the graph whose extension list is to be searched - @param moduleID - the identifier of the module whose extension context is desired - @return void pointer to the extension if found, or NULL if not found. - ********************************************************************/ -void *gp_GetExtension(graphP theGraph, int moduleID) -{ - void *context = NULL; - int result = gp_FindExtension(theGraph, moduleID, &context); - return result ? context : NULL; -} - -/******************************************************************** - gp_RemoveExtension() - @param theGraph - the graph from which to remove an extension - @param moduleID - the ID of the module whose extension context is to be removed - @return OK if the module is successfully removed or not in the list - NOTOK for internal errors, such as invalid parameters - ********************************************************************/ -int gp_RemoveExtension(graphP theGraph, int moduleID) -{ - graphExtensionP prev, curr, next; - - if (theGraph==NULL || moduleID==0) - return NOTOK; - - prev = NULL; - curr = theGraph->extensions; - - while (curr != NULL) - { - next = (graphExtensionP) curr->next; - - if (curr->moduleID == moduleID) - break; - - prev = curr; - curr = next; - } - - // An extension can only be removed if it is found. Otherwise, - // we return OK because the extension degenerately removed - // (since it is already gone) - if (curr != NULL) - { - _FixupFunctionTables(theGraph, curr); - - // Unhook the curr extension - if (prev != NULL) - prev->next = (struct graphExtension *) next; - else theGraph->extensions = next; - - // Free the curr extension - _FreeExtension(curr); - } - - return OK; -} - - -/******************************************************************** - _FixupFunctionTables() - - Removes the functions in the curr function table from the function - call lists established by the function tables of all extensions and - theGraph. - - Since new extensions are prepended, extensions before curr may - have further overloaded the functions in the curr function table. - - For a non-NULL function pointer in the curr table, if there is - a preceding extension with the same function pointer non-NULL, then - the function table of the closest such preceding extension points - to the original overload function of the curr extension, and the - curr extension contains the pointer to the base function behavior, - so now the function table of that preceding extension must be changed - to the function pointer value in the curr extension. - ********************************************************************/ - -void _FixupFunctionTables(graphP theGraph, graphExtensionP curr) -{ - void **currFunctionTable = (void **) (curr->functions); - int numFunctions = sizeof(*(curr->functions)) / sizeof(void *); - int K; - - for (K = 0; K < numFunctions; K++) - { - if (currFunctionTable[K] != NULL) - { - void **nearestOverloadFunctionTable = (void **) &theGraph->functions; - graphExtensionP pred = _FindNearestOverload(theGraph, curr, K); - - if (pred != NULL) - nearestOverloadFunctionTable = (void **) pred->functions; - - nearestOverloadFunctionTable[K] = currFunctionTable[K]; - } - } -} - -/******************************************************************** - _FindNearestOverload() - ********************************************************************/ - -graphExtensionP _FindNearestOverload(graphP theGraph, graphExtensionP target, int functionIndex) -{ - graphExtensionP curr = theGraph->extensions; - graphExtensionP found = NULL; - void **functionTable; - - while (curr != target) - { - functionTable = (void **) curr->functions; - if (functionTable[functionIndex] != NULL) - found = curr; - - curr = (graphExtensionP) curr->next; - } - - return found; -} - -/******************************************************************** - gp_CopyExtensions() - ********************************************************************/ - -int gp_CopyExtensions(graphP dstGraph, graphP srcGraph) -{ - graphExtensionP next = NULL, newNext = NULL, newLast = NULL; - - if (srcGraph == NULL || dstGraph == NULL) - return NOTOK; - - gp_FreeExtensions(dstGraph); - - next = srcGraph->extensions; - - while (next != NULL) - { - if ((newNext = (graphExtensionP) malloc(sizeof(graphExtension))) == NULL) - { - gp_FreeExtensions(dstGraph); - return NOTOK; - } - - newNext->moduleID = next->moduleID; - newNext->context = next->dupContext(next->context, dstGraph); - newNext->dupContext = next->dupContext; - newNext->freeContext = next->freeContext; - newNext->functions = next->functions; - newNext->next = NULL; - - if (newLast != NULL) - newLast->next = (struct graphExtension *) newNext; - else - dstGraph->extensions = newNext; - - newLast = newNext; - next = (graphExtensionP) next->next; - } - - return OK; -} - -/******************************************************************** - gp_FreeExtensions() - - @param pFirst - pointer to head pointer of graph extension list - - Each graph extension is freed, including invoking the freeContext - function provided when the extension was added. - ********************************************************************/ - -void gp_FreeExtensions(graphP theGraph) -{ - if (theGraph != NULL) - { - graphExtensionP curr = theGraph->extensions; - graphExtensionP next = NULL; - - while (curr != NULL) - { - next = (graphExtensionP) curr->next; - _FreeExtension(curr); - curr = next; - } - - theGraph->extensions = NULL; - _InitFunctionTable(theGraph); - } -} - -/******************************************************************** - _FreeExtension() - ********************************************************************/ -void _FreeExtension(graphExtensionP extension) -{ - if (extension->context != NULL && extension->freeContext != NULL) - { - extension->freeContext(extension->context); - } - free(extension); -} - -#ifdef __GNUC__ - #ifndef __clang__ - #pragma GCC diagnostic pop - #endif -#endif \ No newline at end of file diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.h deleted file mode 100644 index 7eb5be533..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef GRAPH_EXTENSIONS_H -#define GRAPH_EXTENSIONS_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "graphStructures.h" - -int gp_AddExtension(graphP theGraph, - int *pModuleID, - void *context, - void *(*dupContext)(void *, void *), - void (*freeContext)(void *), - graphFunctionTableP overloadTable); - -int gp_FindExtension(graphP theGraph, int moduleID, void **pContext); -void *gp_GetExtension(graphP theGraph, int moduleID); - -int gp_RemoveExtension(graphP theGraph, int moduleID); - -int gp_CopyExtensions(graphP dstGraph, graphP srcGraph); - -void gp_FreeExtensions(graphP theGraph); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.private.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.private.h deleted file mode 100644 index 03d708d93..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphExtensions.private.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef GRAPH_EXTENSIONS_PRIVATE_H -#define GRAPH_EXTENSIONS_PRIVATE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graphFunctionTable.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - int moduleID; - void *context; - void *(*dupContext)(void *, void *); - void (*freeContext)(void *); - - graphFunctionTableP functions; - - struct graphExtension *next; -} graphExtension; - -typedef graphExtension * graphExtensionP; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphFunctionTable.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphFunctionTable.h deleted file mode 100644 index 24cbef0b4..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphFunctionTable.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef GRAPHFUNCTIONTABLE_H -#define GRAPHFUNCTIONTABLE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - NOTE: If you add any FUNCTION POINTERS to this function table, then you must - also initialize them in _InitFunctionTable() in graphUtils.c. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -typedef struct -{ - // These function pointers allow extension modules to overload some of - // the behaviors of protected functions. Only advanced applications - // will overload these functions - int (*fpEmbeddingInitialize)(); - void (*fpEmbedBackEdgeToDescendant)(); - void (*fpWalkUp)(); - int (*fpWalkDown)(); - int (*fpMergeBicomps)(); - void (*fpMergeVertex)(); - int (*fpHandleInactiveVertex)(); - int (*fpHandleBlockedBicomp)(); - int (*fpEmbedPostprocess)(); - int (*fpMarkDFSPath)(); - - int (*fpCheckEmbeddingIntegrity)(); - int (*fpCheckObstructionIntegrity)(); - - // These function pointers allow extension modules to overload some - // of the behaviors of gp_* function in the public API - int (*fpInitGraph)(); - void (*fpReinitializeGraph)(); - int (*fpEnsureArcCapacity)(); - int (*fpSortVertices)(); - - int (*fpReadPostprocess)(); - int (*fpWritePostprocess)(); - - void (*fpHideEdge)(); - void (*fpRestoreEdge)(); - int (*fpHideVertex)(); - int (*fpRestoreVertex)(); - int (*fpContractEdge)(); - int (*fpIdentifyVertices)(); - -} graphFunctionTable; - -typedef graphFunctionTable * graphFunctionTableP; - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIO.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIO.c deleted file mode 100644 index 9b805ed3d..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIO.c +++ /dev/null @@ -1,774 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -#pragma GCC diagnostic ignored "-Wunused-result" -#endif - -#include -#include - -#include "graph.h" - -/* Private functions (exported to system) */ - -int _ReadAdjMatrix(graphP theGraph, FILE *Infile); -int _ReadAdjList(graphP theGraph, FILE *Infile); -int _WriteAdjList(graphP theGraph, FILE *Outfile); -int _WriteAdjMatrix(graphP theGraph, FILE *Outfile); -int _WriteDebugInfo(graphP theGraph, FILE *Outfile); - -/******************************************************************** - _ReadAdjMatrix() - This function reads the undirected graph in upper triangular matrix format. - Though O(N^2) time is required, this routine is useful during - reliability testing due to the wealth of graph generating software - that uses this format for output. - Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges - ********************************************************************/ - -int _ReadAdjMatrix(graphP theGraph, FILE *Infile) -{ - int N, v, w, Flag; - - if (Infile == NULL) return NOTOK; - fscanf(Infile, " %d ", &N); - if (gp_InitGraph(theGraph, N) != OK) - return NOTOK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - gp_SetVertexIndex(theGraph, v, v); - for (w = v+1; gp_VertexInRange(theGraph, w); w++) - { - fscanf(Infile, " %1d", &Flag); - if (Flag) - { - if (gp_AddEdge(theGraph, v, 0, w, 0) != OK) - return NOTOK; - } - } - } - - return OK; -} - -/******************************************************************** - _ReadAdjList() - This function reads the graph in adjacency list format. - - The file format is - On the first line : N= number of vertices - On N subsequent lines: #: a b c ... -1 - where # is a vertex number and a, b, c, ... are its neighbors. - - NOTE: The vertex number is for file documentation only. It is an - error if the vertices are not in sorted order in the file. - - NOTE: If a loop edge is found, it is ignored without error. - - NOTE: This routine supports digraphs. For a directed arc (v -> W), - an edge record is created in both vertices, v and W, and the - edge record in v's adjacency list is marked OUTONLY while the - edge record in W's list is marked INONLY. - This makes it easy to used edge directedness when appropriate - but also seamlessly process the corresponding undirected graph. - - Returns: OK on success, NONEMBEDDABLE if success except too many edges - NOTOK on file content error (or internal error) - ********************************************************************/ - -int _ReadAdjList(graphP theGraph, FILE *Infile) -{ - int N, v, W, adjList, e, indexValue, ErrorCode; - int zeroBased = FALSE; - - if (Infile == NULL) return NOTOK; - fgetc(Infile); /* Skip the N= */ - fgetc(Infile); - fscanf(Infile, " %d ", &N); /* Read N */ - if (gp_InitGraph(theGraph, N) != OK) - { - printf("Failed to init graph"); - return NOTOK; - } - - // Clear the visited members of the vertices so they can be used - // during the adjacency list read operation - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_SetVertexVisitedInfo(theGraph, v, NIL); - - // Do the adjacency list read operation for each vertex in order - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - // Read the vertex number - fscanf(Infile, "%d", &indexValue); - - if (indexValue == 0 && v == gp_GetFirstVertex(theGraph)) - zeroBased = TRUE; - indexValue += zeroBased ? gp_GetFirstVertex(theGraph) : 0; - - gp_SetVertexIndex(theGraph, v, indexValue); - - // The vertices are expected to be in numeric ascending order - if (gp_GetVertexIndex(theGraph, v) != v) - return NOTOK; - - // Skip the colon after the vertex number - fgetc(Infile); - - // If the vertex already has a non-empty adjacency list, then it is - // the result of adding edges during processing of preceding vertices. - // The list is removed from the current vertex v and saved for use - // during the read operation for v. Adjacencies to preceding vertices - // are pulled from this list, if present, or added as directed edges - // if not. Adjacencies to succeeding vertices are added as undirected - // edges, and will be corrected later if the succeeding vertex does not - // have the matching adjacency using the following mechanism. After the - // read operation for a vertex v, any adjacency nodes left in the saved - // list are converted to directed edges from the preceding vertex to v. - adjList = gp_GetFirstArc(theGraph, v); - if (gp_IsArc(adjList)) - { - // Store the adjacency node location in the visited member of each - // of the preceding vertices to which v is adjacent so that we can - // efficiently detect the adjacency during the read operation and - // efficiently find the adjacency node. - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), e); - e = gp_GetNextArc(theGraph, e); - } - - // Make the adjacency list circular, for later ease of processing - gp_SetPrevArc(theGraph, adjList, gp_GetLastArc(theGraph, v)); - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), adjList); - - // Remove the list from the vertex - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - } - - // Read the adjacency list. - while (1) - { - // Read the value indicating the next adjacent vertex (or the list end) - fscanf(Infile, " %d ", &W); - W += zeroBased ? gp_GetFirstVertex(theGraph) : 0; - - // A value below the valid range indicates the adjacency list end - if (W < gp_GetFirstVertex(theGraph)) - break; - - // A value above the valid range is an error - if (W > gp_GetLastVertex(theGraph)) - return NOTOK; - - // Loop edges are not supported - else if (W == v) - return NOTOK; - - // If the adjacency is to a succeeding, higher numbered vertex, - // then we'll add an undirected edge for now - else if (v < W) - { - if ((ErrorCode = gp_AddEdge(theGraph, v, 0, W, 0)) != OK) - return ErrorCode; - } - - // If the adjacency is to a preceding, lower numbered vertex, then - // we have to pull the adjacency node from the preexisting adjList, - // if it is there, and if not then we have to add a directed edge. - else - { - // If the adjacency node (arc) already exists, then we add it - // as the new first arc of the vertex and delete it from adjList - if (gp_IsArc(gp_GetVertexVisitedInfo(theGraph, W))) - { - e = gp_GetVertexVisitedInfo(theGraph, W); - - // Remove the arc e from the adjList construct - gp_SetVertexVisitedInfo(theGraph, W, NIL); - if (adjList == e) - { - if ((adjList = gp_GetNextArc(theGraph, e)) == e) - adjList = NIL; - } - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e)); - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e)); - - gp_AttachFirstArc(theGraph, v, e); - } - - // If an adjacency node to the lower numbered vertex W does not - // already exist, then we make a new directed arc from the current - // vertex v to W. - else - { - // It is added as the new first arc in both vertices - if ((ErrorCode = gp_AddEdge(theGraph, v, 0, W, 0)) != OK) - return ErrorCode; - - // Note that this call also sets OUTONLY on the twin arc - gp_SetDirection(theGraph, gp_GetFirstArc(theGraph, W), EDGEFLAG_DIRECTION_INONLY); - } - } - } - - // If there are still adjList entries after the read operation - // then those entries are not representative of full undirected edges. - // Rather, they represent incoming directed arcs from other vertices - // into vertex v. They need to be added back into v's adjacency list but - // marked as "INONLY", while the twin is marked "OUTONLY" (by the same function). - while (gp_IsArc(adjList)) - { - e = adjList; - - gp_SetVertexVisitedInfo(theGraph, gp_GetNeighbor(theGraph, e), NIL); - - if ((adjList = gp_GetNextArc(theGraph, e)) == e) - adjList = NIL; - - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, e), gp_GetPrevArc(theGraph, e)); - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, e), gp_GetNextArc(theGraph, e)); - - gp_AttachFirstArc(theGraph, v, e); - gp_SetDirection(theGraph, e, EDGEFLAG_DIRECTION_INONLY); - } - } - - if (zeroBased) - theGraph->internalFlags |= FLAGS_ZEROBASEDIO; - - return OK; -} - -/******************************************************************** - _ReadLEDAGraph() - Reads the edge list from a LEDA file containing a simple undirected graph. - LEDA files use a one-based numbering system, which is converted to - zero-based numbers if the graph reports starting at zero as the first vertex. - - Returns: OK on success, NONEMBEDDABLE if success except too many edges - NOTOK on file content error (or internal error) - ********************************************************************/ - -int _ReadLEDAGraph(graphP theGraph, FILE *Infile) -{ - char Line[256]; - int N, M, m, u, v, ErrorCode; - int zeroBasedOffset = gp_GetFirstVertex(theGraph)==0 ? 1 : 0; - - /* Skip the lines that say LEDA.GRAPH and give the node and edge types */ - fgets(Line, 255, Infile); - fgets(Line, 255, Infile); - fgets(Line, 255, Infile); - - /* Read the number of vertices N, initialize the graph, then skip N. */ - fgets(Line, 255, Infile); - sscanf(Line, " %d", &N); - - if (gp_InitGraph(theGraph, N) != OK) - return NOTOK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - fgets(Line, 255, Infile); - - /* Read the number of edges */ - fgets(Line, 255, Infile); - sscanf(Line, " %d", &M); - - /* Read and add each edge, omitting loops and parallel edges */ - for (m = 0; m < M; m++) - { - fgets(Line, 255, Infile); - sscanf(Line, " %d %d", &u, &v); - if (u != v && !gp_IsNeighbor(theGraph, u-zeroBasedOffset, v-zeroBasedOffset)) - { - if ((ErrorCode = gp_AddEdge(theGraph, u-zeroBasedOffset, 0, v-zeroBasedOffset, 0)) != OK) - return ErrorCode; - } - } - - if (zeroBasedOffset) - theGraph->internalFlags |= FLAGS_ZEROBASEDIO; - - return OK; -} - -/******************************************************************** - gp_Read() - Opens the given file, determines whether it is in adjacency list or - matrix format based on whether the file start with N or just a number, - calls the appropriate read function, then closes the file and returns - the graph. - - Digraphs and loop edges are not supported in the adjacency matrix format, - which is upper triangular. - - In the adjacency list format, digraphs are supported. Loop edges are - ignored without producing an error. - - Pass "stdin" for the FileName to read from the stdin stream - - Returns: OK, NOTOK on internal error, NONEMBEDDABLE if too many edges - ********************************************************************/ - -int gp_Read(graphP theGraph, char *FileName) -{ -FILE *Infile; -char Ch; -int RetVal; - - if (strcmp(FileName, "stdin") == 0) - Infile = stdin; - else if ((Infile = fopen(FileName, READTEXT)) == NULL) - return NOTOK; - - Ch = (char) fgetc(Infile); - ungetc(Ch, Infile); - if (Ch == 'N') - RetVal = _ReadAdjList(theGraph, Infile); - else if (Ch == 'L') - RetVal = _ReadLEDAGraph(theGraph, Infile); - else RetVal = _ReadAdjMatrix(theGraph, Infile); - - if (RetVal == OK) - { - void *extraData = NULL; - long filePos = ftell(Infile); - long fileSize; - - fseek(Infile, 0, SEEK_END); - fileSize = ftell(Infile); - fseek(Infile, filePos, SEEK_SET); - - if (filePos < fileSize) - { - extraData = malloc(fileSize - filePos + 1); - fread(extraData, fileSize - filePos, 1, Infile); - } -/*// Useful for quick debugging of IO extensibility - if (extraData == NULL) - printf("extraData == NULL\n"); - else - { - char *extraDataString = (char *) extraData; - extraDataString[fileSize - filePos] = '\0'; - printf("extraData = '%s'\n", extraDataString); - } -*/ - - if (extraData != NULL) - { - RetVal = theGraph->functions.fpReadPostprocess(theGraph, extraData, fileSize - filePos); - free((void *) extraData); - } - } - - if (strcmp(FileName, "stdin") != 0) - fclose(Infile); - - return RetVal; -} - -int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize) -{ - return OK; -} - -/******************************************************************** - _WriteAdjList() - For each vertex, we write its number, a colon, the list of adjacent vertices, - then a NIL. The vertices occupy the first N positions of theGraph. Each - vertex is also has indicators of the first and last adjacency nodes (arcs) - in its adjacency list. - - Returns: NOTOK if either param is NULL; OK otherwise (after printing - adjacency list representation to Outfile). - ********************************************************************/ - -int _WriteAdjList(graphP theGraph, FILE *Outfile) -{ - int v, e; - int zeroBasedOffset = (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? gp_GetFirstVertex(theGraph) : 0; - - if (theGraph==NULL || Outfile==NULL) return NOTOK; - - fprintf(Outfile, "N=%d\n", theGraph->N); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - fprintf(Outfile, "%d:", v - zeroBasedOffset); - - e = gp_GetLastArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) - fprintf(Outfile, " %d", gp_GetNeighbor(theGraph, e) - zeroBasedOffset); - - e = gp_GetPrevArc(theGraph, e); - } - - // Write NIL at the end of the adjacency list (in zero-based I/O, NIL was -1) - fprintf(Outfile, " %d\n", (theGraph->internalFlags & FLAGS_ZEROBASEDIO) ? -1 : NIL); - } - return OK; -} - -/******************************************************************** - _WriteAdjMatrix() - Outputs upper triangular matrix representation capable of being - read by _ReadAdjMatrix() - - Note: This routine does not support digraphs and will return an - error if a directed edge is found. - - returns OK for success, NOTOK for failure - ********************************************************************/ - -int _WriteAdjMatrix(graphP theGraph, FILE *Outfile) -{ -int v, e, K; -char *Row = NULL; - - if (theGraph != NULL) - Row = (char *) malloc((theGraph->N+1)*sizeof(char)); - - if (Row==NULL || theGraph==NULL || Outfile==NULL) - { - if (Row != NULL) free(Row); - return NOTOK; - } - - fprintf(Outfile, "%d\n", theGraph->N); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - for (K = gp_GetFirstVertex(theGraph); K <= v; K++) - Row[K - gp_GetFirstVertex(theGraph)] = ' '; - for (K = v+1; gp_VertexInRange(theGraph, K); K++) - Row[K - gp_GetFirstVertex(theGraph)] = '0'; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) == EDGEFLAG_DIRECTION_INONLY) - return NOTOK; - - if (gp_GetNeighbor(theGraph, e) > v) - Row[gp_GetNeighbor(theGraph, e) - gp_GetFirstVertex(theGraph)] = '1'; - - e = gp_GetNextArc(theGraph, e); - } - - Row[theGraph->N] = '\0'; - fprintf(Outfile, "%s\n", Row); - } - - free(Row); - return OK; -} - -/******************************************************************** - ********************************************************************/ - -char _GetEdgeTypeChar(graphP theGraph, int e) -{ - char type = 'U'; - - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - type = 'C'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_FORWARD) - type = 'F'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - type = 'P'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_BACK) - type = 'B'; - else if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_RANDOMTREE) - type = 'T'; - - return type; -} - -/******************************************************************** - ********************************************************************/ - -char _GetVertexObstructionTypeChar(graphP theGraph, int v) -{ - char type = 'U'; - - if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW) - type = 'X'; - else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - type = 'x'; - if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW) - type = 'Y'; - else if (gp_GetVertexObstructionType(theGraph, v) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - type = 'y'; - - return type; -} - -/******************************************************************** - _WriteDebugInfo() - Writes adjacency list, but also includes the type value of each - edge (e.g. is it DFS child arc, forward arc or back arc?), and - the L, A and DFSParent of each vertex. - ********************************************************************/ - -int _WriteDebugInfo(graphP theGraph, FILE *Outfile) -{ -int v, e, EsizeOccupied; - - if (theGraph==NULL || Outfile==NULL) return NOTOK; - - /* Print parent copy vertices and their adjacency lists */ - - fprintf(Outfile, "DEBUG N=%d M=%d\n", theGraph->N, theGraph->M); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - fprintf(Outfile, "%d(P=%d,lA=%d,LowPt=%d,v=%d):", - v, gp_GetVertexParent(theGraph, v), - gp_GetVertexLeastAncestor(theGraph, v), - gp_GetVertexLowpoint(theGraph, v), - gp_GetVertexIndex(theGraph, v)); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - fprintf(Outfile, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e); - e = gp_GetNextArc(theGraph, e); - } - - fprintf(Outfile, " %d\n", NIL); - } - - /* Print any root copy vertices and their adjacency lists */ - - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - { - if (!gp_VirtualVertexInUse(theGraph, v)) - continue; - - fprintf(Outfile, "%d(copy of=%d, DFS child=%d):", - v, gp_GetVertexIndex(theGraph, v), - gp_GetDFSChildFromRoot(theGraph, v)); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - fprintf(Outfile, " %d(e=%d)", gp_GetNeighbor(theGraph, e), e); - e = gp_GetNextArc(theGraph, e); - } - - fprintf(Outfile, " %d\n", NIL); - } - - /* Print information about vertices and root copy (virtual) vertices */ - fprintf(Outfile, "\nVERTEX INFORMATION\n"); - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - fprintf(Outfile, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n", - v, - gp_GetVertexIndex(theGraph, v), - (gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)), - gp_GetFirstArc(theGraph, v), - gp_GetLastArc(theGraph, v)); - } - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - { - if (gp_VirtualVertexNotInUse(theGraph, v)) - continue; - - fprintf(Outfile, "V[%3d] index=%3d, type=%c, first arc=%3d, last arc=%3d\n", - v, - gp_GetVertexIndex(theGraph, v), - (gp_IsVirtualVertex(theGraph, v) ? 'X' : _GetVertexObstructionTypeChar(theGraph, v)), - gp_GetFirstArc(theGraph, v), - gp_GetLastArc(theGraph, v)); - } - - /* Print information about edges */ - - fprintf(Outfile, "\nEDGE INFORMATION\n"); - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - { - if (gp_EdgeInUse(theGraph, e)) - { - fprintf(Outfile, "E[%3d] neighbor=%3d, type=%c, next arc=%3d, prev arc=%3d\n", - e, - gp_GetNeighbor(theGraph, e), - _GetEdgeTypeChar(theGraph, e), - gp_GetNextArc(theGraph, e), - gp_GetPrevArc(theGraph, e)); - } - } - - return OK; -} - -/******************************************************************** - gp_Write() - Writes theGraph into the file. - Pass "stdout" or "stderr" to FileName to write to the corresponding stream - Pass WRITE_ADJLIST, WRITE_ADJMATRIX or WRITE_DEBUGINFO for the Mode - - NOTE: For digraphs, it is an error to use a mode other than WRITE_ADJLIST - - Returns NOTOK on error, OK on success. - ********************************************************************/ - -int gp_Write(graphP theGraph, char *FileName, int Mode) -{ -FILE *Outfile; -int RetVal; - - if (theGraph == NULL || FileName == NULL) - return NOTOK; - - if (strcmp(FileName, "nullwrite") == 0) - return OK; - - if (strcmp(FileName, "stdout") == 0) - Outfile = stdout; - else if (strcmp(FileName, "stderr") == 0) - Outfile = stderr; - else if ((Outfile = fopen(FileName, WRITETEXT)) == NULL) - return NOTOK; - - switch (Mode) - { - case WRITE_ADJLIST : - RetVal = _WriteAdjList(theGraph, Outfile); - break; - case WRITE_ADJMATRIX : - RetVal = _WriteAdjMatrix(theGraph, Outfile); - break; - case WRITE_DEBUGINFO : - RetVal = _WriteDebugInfo(theGraph, Outfile); - break; - default : - RetVal = NOTOK; - break; - } - - if (RetVal == OK) - { - void *extraData = NULL; - long extraDataSize; - - RetVal = theGraph->functions.fpWritePostprocess(theGraph, &extraData, &extraDataSize); - - if (extraData != NULL) - { - if (!fwrite(extraData, extraDataSize, 1, Outfile)) - RetVal = NOTOK; - free(extraData); - } - } - - if (strcmp(FileName, "stdout") == 0 || strcmp(FileName, "stderr") == 0) - fflush(Outfile); - - else if (fclose(Outfile) != 0) - RetVal = NOTOK; - - return RetVal; -} - -/******************************************************************** - _WritePostprocess() - - By default, no additional information is written. - ********************************************************************/ - -int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize) -{ - return OK; -} - -/******************************************************************** - _Log() - - When the project is compiled with LOGGING enabled, this method writes - a string to the file PLANARITY.LOG in the current working directory. - On first write, the file is created or cleared. - Call this method with NULL to close the log file. - ********************************************************************/ - -void _Log(char *Str) -{ -static FILE *logfile = NULL; - - if (logfile == NULL) - { - if ((logfile = fopen("PLANARITY.LOG", WRITETEXT)) == NULL) - return; - } - - if (Str != NULL) - { - fprintf(logfile, "%s", Str); - fflush(logfile); - } - else - fclose(logfile); -} - -void _LogLine(char *Str) -{ - _Log(Str); - _Log("\n"); -} - -static char LogStr[512]; - -char *_MakeLogStr1(char *format, int one) -{ - sprintf(LogStr, format, one); - return LogStr; -} - -char *_MakeLogStr2(char *format, int one, int two) -{ - sprintf(LogStr, format, one, two); - return LogStr; -} - -char *_MakeLogStr3(char *format, int one, int two, int three) -{ - sprintf(LogStr, format, one, two, three); - return LogStr; -} - -char *_MakeLogStr4(char *format, int one, int two, int three, int four) -{ - sprintf(LogStr, format, one, two, three, four); - return LogStr; -} - -char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five) -{ - sprintf(LogStr, format, one, two, three, four, five); - return LogStr; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIsolator.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIsolator.c deleted file mode 100644 index 5f5b0560a..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphIsolator.c +++ /dev/null @@ -1,840 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif -#define GRAPHISOLATOR_C - -#include "graph.h" - -/* Imported functions */ - -extern void _ClearVisitedFlags(graphP); - -extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -extern int _JoinBicomps(graphP theGraph); - -extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); - -/* Private function declarations (exported within system) */ - -int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); - -int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, - int *pAncestor, int *pDescendant); -int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, - int *pDescendant); -int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, - int SubtreeRoot, int *pDescendant); - -int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); - -int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); -void _AddBackEdge(graphP theGraph, int ancestor, int descendant); -int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -int _InitializeIsolatorContext(graphP theGraph); - -int _IsolateMinorA(graphP theGraph); -int _IsolateMinorB(graphP theGraph); -int _IsolateMinorC(graphP theGraph); -int _IsolateMinorD(graphP theGraph); -int _IsolateMinorE(graphP theGraph); - -int _IsolateMinorE1(graphP theGraph); -int _IsolateMinorE2(graphP theGraph); -int _IsolateMinorE3(graphP theGraph); -int _IsolateMinorE4(graphP theGraph); - -int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); -int _MarkDFSPathsToDescendants(graphP theGraph); -int _AddAndMarkUnembeddedEdges(graphP theGraph); - -/**************************************************************************** - gp_IsolateKuratowskiSubgraph() - ****************************************************************************/ - -int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R) -{ -int RetVal; - -/* A subgraph homeomorphic to K_{3,3} or K_5 will be isolated by using the visited - flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we - subsequently set visited on all edges and vertices in the homeomorph. */ - - _ClearVisitedFlags(theGraph); - -/* Next, we determine which of the non-planarity Minors was encountered - and the principal bicomp on which the isolator will focus attention. */ - - if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - - if (_InitializeIsolatorContext(theGraph) != OK) - return NOTOK; - -/* Call the appropriate isolator */ - - if (theGraph->IC.minorType & MINORTYPE_A) - RetVal = _IsolateMinorA(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_B) - RetVal = _IsolateMinorB(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_C) - RetVal = _IsolateMinorC(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_D) - RetVal = _IsolateMinorD(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_E) - RetVal = _IsolateMinorE(theGraph); - else - RetVal = NOTOK; - -/* Delete the unmarked edges and vertices, and return */ - - if (RetVal == OK) - RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph); - - return RetVal; -} - -/**************************************************************************** - _InitializeIsolatorContext() - ****************************************************************************/ - -int _InitializeIsolatorContext(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - -/* Obtains the edges connecting X and Y to ancestors of the current vertex */ - - if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE || - _FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE) - return NOTOK; - -/* For Minor B, we seek the last pertinent child biconnected component, which - is also future pertinent, and obtain the DFS child in its root edge. - This child is the subtree root containing vertices with connections to - both the current vertex and an ancestor of the current vertex. */ - - if (theGraph->IC.minorType & MINORTYPE_B) - { - int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); - - IC->uz = gp_GetVertexLowpoint(theGraph, SubtreeRoot); - - if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE || - _FindUnembeddedEdgeToSubtree(theGraph, IC->uz, SubtreeRoot, &IC->dz) != TRUE) - return NOTOK; - } - -/* For all other minors, we obtain an unembedded connecting the current vertex to the - pertinent vertex W, and for minor E we collect the additional unembedded ancestor - connection for the future pertinent vertex Z. */ - - else - { - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - if (theGraph->IC.minorType & MINORTYPE_E) - if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE) - return NOTOK; - } - - return OK; -} - -/**************************************************************************** - _IsolateMinorA(): Isolate a K3,3 homeomorph - ****************************************************************************/ - -int _IsolateMinorA(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorB(): Isolate a K3,3 homeomorph - ****************************************************************************/ - -int _IsolateMinorB(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux,IC->uy,IC->uz), - MAX3(IC->ux,IC->uy,IC->uz)) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorC(): Isolate a K3,3 homeomorph - ****************************************************************************/ - -int _IsolateMinorC(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (gp_GetVertexObstructionType(theGraph, IC->px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW) - { - int highY = gp_GetVertexObstructionType(theGraph, IC->py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW - ? IC->py : IC->y; - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, highY) != OK) - return NOTOK; - } - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->r) != OK) - return NOTOK; - } - - // Note: The x-y path is already marked, due to identifying the type of non-planarity minor - if (_MarkDFSPathsToDescendants(theGraph) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorD(): Isolate a K3,3 homeomorph - ****************************************************************************/ - -int _IsolateMinorD(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - // Note: The x-y and v-z paths are already marked, due to identifying the type of non-planarity minor - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->y) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE() - ****************************************************************************/ - -int _IsolateMinorE(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - -/* Minor E1: Isolate a K3,3 homeomorph */ - - if (IC->z != IC->w) - return _IsolateMinorE1(theGraph); - -/* Minor E2: Isolate a K3,3 homeomorph */ - - if (IC->uz > MAX(IC->ux, IC->uy)) - return _IsolateMinorE2(theGraph); - -/* Minor E3: Isolate a K3,3 homeomorph */ - - if (IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) - return _IsolateMinorE3(theGraph); - -/* Minor E4: Isolate a K3,3 homeomorph */ - - else if (IC->x != IC->px || IC->y != IC->py) - return _IsolateMinorE4(theGraph); - -/* Minor E: Isolate a K5 homeomorph */ - - // Note: The x-y path is already marked, due to identifying the type of non-planarity minor - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), IC->r) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE1() - - Reduce to Minor C if the vertex Z responsible for external activity - below the X-Y path does not equal the pertinent vertex W. - ****************************************************************************/ - -int _IsolateMinorE1(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (gp_GetVertexObstructionType(theGraph, IC->z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - { - gp_ResetVertexObstructionType(theGraph, IC->px, VERTEX_OBSTRUCTIONTYPE_HIGH_RXW); - IC->x=IC->z; IC->ux=IC->uz; IC->dx=IC->dz; - } - else if (gp_GetVertexObstructionType(theGraph, IC->z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - { - gp_ResetVertexObstructionType(theGraph, IC->py, VERTEX_OBSTRUCTIONTYPE_HIGH_RYW); - IC->y=IC->z; IC->uy=IC->uz; IC->dy=IC->dz; - } - else return NOTOK; - - // Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor, - // but the x-y path is also included in minor C, so we let it stay marked since the minor C - // isolator also assumes the x-y path has been marked by non-planarity minor type identification - IC->z = IC->uz = IC->dz = NIL; - theGraph->IC.minorType ^= MINORTYPE_E; - theGraph->IC.minorType |= (MINORTYPE_C|MINORTYPE_E1); - return _IsolateMinorC(theGraph); -} - -/**************************************************************************** - _IsolateMinorE2() - - If uZ (which is the ancestor of v that is adjacent to Z) is a - descendant of both uY and uX, then we reduce to Minor A - ****************************************************************************/ - -int _IsolateMinorE2(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - // Note: The x-y path was already marked, due to identifying E as the type of non-planarity minor, - // but we're reducing to Minor A, which does not include the x-y path, so the visited flags are - // cleared as a convenient, if somewhat wasteful, way to clear the marking on the x-y path - _ClearVisitedFlags(theGraph); - - IC->v = IC->uz; - IC->dw = IC->dz; - IC->z = IC->uz = IC->dz = NIL; - - theGraph->IC.minorType ^= MINORTYPE_E; - theGraph->IC.minorType |= (MINORTYPE_A|MINORTYPE_E2); - return _IsolateMinorA(theGraph); -} - -/**************************************************************************** - _IsolateMinorE3() - ****************************************************************************/ - -int _IsolateMinorE3(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (IC->ux < IC->uy) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->px) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) - return NOTOK; - } - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK) - return NOTOK; - } - - // Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor - if (theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), IC->r) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - theGraph->IC.minorType |= MINORTYPE_E3; - return OK; -} - -/**************************************************************************** - _IsolateMinorE4() - ****************************************************************************/ - -int _IsolateMinorE4(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (IC->px != IC->x) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->w) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK) - return NOTOK; - } - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->px) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->r) != OK) - return NOTOK; - } - - // Note: The x-y path is already marked, due to identifying E as the type of non-planarity minor - if (theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux, IC->uy, IC->uz), - MAX3(IC->ux, IC->uy, IC->uz)) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - theGraph->IC.minorType |= MINORTYPE_E4; - return OK; -} - -/**************************************************************************** - _GetLeastAncestorConnection() - - This function searches for an ancestor of the current vertex v adjacent by a - cycle edge to the given cutVertex or one of its DFS descendants appearing in - a separated bicomp. The given cutVertex is assumed to be future pertinent - such that either the leastAncestor or the lowpoint of a separated DFS child - is less than v. We obtain the minimum possible connection from the cutVertex - to an ancestor of v. - ****************************************************************************/ - -int _GetLeastAncestorConnection(graphP theGraph, int cutVertex) -{ - int child; - int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); - - child = gp_GetVertexFuturePertinentChild(theGraph, cutVertex); - while (gp_IsVertex(child)) - { - if (gp_IsSeparatedDFSChild(theGraph, child) && - ancestor > gp_GetVertexLowpoint(theGraph, child)) - ancestor = gp_GetVertexLowpoint(theGraph, child); - - child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); - } - - return ancestor; -} - -/**************************************************************************** - _FindUnembeddedEdgeToAncestor() - - This function searches for an ancestor of the current vertex v adjacent by a - cycle edge to the given cutVertex or one of its DFS descendants appearing in - a separated bicomp. - - The given cutVertex is assumed to be future pertinent such that either the - leastAncestor or the lowpoint of a separated DFS child is less than v. - We obtain the minimum possible connection from the cutVertex to an ancestor - of v, then compute the descendant accordingly. - - Returns TRUE if found, FALSE otherwise. - ****************************************************************************/ - -int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, - int *pAncestor, int *pDescendant) -{ - int child, foundChild; - int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); - - child = gp_GetVertexFuturePertinentChild(theGraph, cutVertex); - foundChild = NIL; - while (gp_IsVertex(child)) - { - if (gp_IsSeparatedDFSChild(theGraph, child) && - ancestor > gp_GetVertexLowpoint(theGraph, child)) - { - ancestor = gp_GetVertexLowpoint(theGraph, child); - foundChild = child; - } - child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); - } - - *pAncestor = ancestor; - - // If the least ancestor connection was direct, then return the cutVertex as the descendant - if (ancestor == gp_GetVertexLeastAncestor(theGraph, cutVertex)) - { - *pDescendant = cutVertex; - return TRUE; - } - - // Otherwise find the descendant based on the separated child with least lowpoint - return _FindUnembeddedEdgeToSubtree(theGraph, *pAncestor, foundChild, pDescendant); -} - -/**************************************************************************** - _FindUnembeddedEdgeToCurVertex() - - Given the current vertex v, we search for an edge connecting v to either - a given pertinent vertex W or one of its DFS descendants in the subtree - indicated by the the last pertinent child biconnected component. - Returns TRUE if founds, FALSE otherwise. - ****************************************************************************/ - -int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant) -{ - if (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, cutVertex))) - { - *pDescendant = cutVertex; - return TRUE; - } - else - { - int subtreeRoot = gp_GetVertexFirstPertinentRootChild(theGraph, cutVertex); - - return _FindUnembeddedEdgeToSubtree(theGraph, theGraph->IC.v, - subtreeRoot, pDescendant); - } -} - -/**************************************************************************** - _FindUnembeddedEdgeToSubtree() - - Given the root vertex of a DFS subtree and an ancestor of that subtree, - find a vertex in the subtree that is adjacent to the ancestor by a - cycle edge. - Returns TRUE if found, FALSE if not found. - ****************************************************************************/ - -int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, - int SubtreeRoot, int *pDescendant) -{ -int e, Z, ZNew; - - *pDescendant = NIL; - -/* If SubtreeRoot is a root copy, then we change to the DFS child in the - DFS tree root edge of the bicomp rooted by SubtreeRoot. */ - - SubtreeRoot = gp_IsVirtualVertex(theGraph, SubtreeRoot) - ? gp_GetDFSChildFromRoot(theGraph, SubtreeRoot) - : SubtreeRoot; - -/* Find the least descendant of the cut vertex incident to the ancestor. */ - - e = gp_GetVertexFwdArcList(theGraph, ancestor); - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) >= SubtreeRoot) - { - if (gp_IsNotVertex(*pDescendant) || *pDescendant > gp_GetNeighbor(theGraph, e)) - *pDescendant = gp_GetNeighbor(theGraph, e); - } - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, ancestor)) - e = NIL; - } - - if (gp_IsNotVertex(*pDescendant)) - return FALSE; - -/* Make sure the identified descendant actually descends from the cut vertex */ - - Z = *pDescendant; - while (Z != SubtreeRoot) - { - ZNew = gp_GetVertexParent(theGraph, Z); - if (gp_IsNotVertex(ZNew) || ZNew == Z) - return FALSE; - Z = ZNew; - } - -/* Return successfully */ - - return TRUE; -} - - -/**************************************************************************** - _MarkPathAlongBicompExtFace() - - Sets the visited flags of vertices and edges on the external face of a - bicomp from startVert to endVert, inclusive, by following the 'first' arc - link out of each visited vertex. - ****************************************************************************/ - -int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert) -{ -int Z, ZPrevLink, ZPrevArc; - -/* Mark the start vertex (and if it is a root copy, mark the parent copy too. */ - - gp_SetVertexVisited(theGraph, startVert); - -/* For each vertex visited after the start vertex, mark the vertex and the - edge used to get there. Stop after marking the ending vertex. */ - - Z = startVert; - ZPrevLink = 1; - do { - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - - ZPrevArc = gp_GetArc(theGraph, Z, ZPrevLink); - - gp_SetEdgeVisited(theGraph, ZPrevArc); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, ZPrevArc)); - gp_SetVertexVisited(theGraph, Z); - - } while (Z != endVert); - - return OK; -} - -/**************************************************************************** - _MarkDFSPath() - - Sets visited flags of vertices and edges from descendant to ancestor, - including root copy vertices, and including the step of hopping from - a root copy to its parent copy. - - At each vertex, the edge record is obtained whose type indicates that it - leads to the DFS parent. An earlier implementation just used the DFS parent - member of the vertex, but then had to find the edge to mark anyway. - This method is more generalized because some extension algorithms reduce - DFS paths to single DFS tree edges, in which case the edge record with type - EDGE_TYPE_PARENT may indicate the DFS paent or an ancestor. - ****************************************************************************/ -int _MarkDFSPath(graphP theGraph, int ancestor, int descendant) -{ -int e, parent; - - // If we are marking from a root (virtual) vertex upward, then go up to the parent - // copy before starting the loop - if (gp_IsVirtualVertex(theGraph, descendant)) - descendant = gp_GetPrimaryVertexFromRoot(theGraph, descendant); - - // Mark the lowest vertex (the one with the highest number). - gp_SetVertexVisited(theGraph, descendant); - - // Mark all ancestors of the lowest vertex, and the edges used to reach - // them, up to the given ancestor vertex. - while (descendant != ancestor) - { - if (gp_IsNotVertex(descendant)) - return NOTOK; - - // If we are at a bicomp root, then ascend to its parent copy and - // mark it as visited. - if (gp_IsVirtualVertex(theGraph, descendant)) - { - parent = gp_GetPrimaryVertexFromRoot(theGraph, descendant); - } - - // If we are on a regular, non-virtual vertex then get the edge to the parent, - // mark the edge, then fall through to the code that marks the parent vertex. - else - { - // Scan the edges for the one marked as the DFS parent - parent = NIL; - e = gp_GetFirstArc(theGraph, descendant); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - { - parent = gp_GetNeighbor(theGraph, e); - break; - } - e = gp_GetNextArc(theGraph, e); - } - - // Sanity check on the data structure integrity - if (gp_IsNotVertex(parent)) - return NOTOK; - - // Mark the edge - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - // Mark the parent, then hop to the parent and reiterate - gp_SetVertexVisited(theGraph, parent); - descendant = parent; - } - - return OK; -} - -/**************************************************************************** - _MarkDFSPathsToDescendants() - ****************************************************************************/ - -int _MarkDFSPathsToDescendants(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK) - return NOTOK; - - if (gp_IsVertex(IC->dw)) - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK) - return NOTOK; - - if (gp_IsVertex(IC->dz)) - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dz) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _AddAndMarkUnembeddedEdges() - ****************************************************************************/ - -int _AddAndMarkUnembeddedEdges(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK || - _AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK) - return NOTOK; - - if (gp_IsVertex(IC->dw)) - if (_AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - if (gp_IsVertex(IC->dz)) - if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _AddAndMarkEdge() - - Adds edge records for the edge (ancestor, descendant) and marks the edge - records and vertex structures that represent the edge. - ****************************************************************************/ - -int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant) -{ - _AddBackEdge(theGraph, ancestor, descendant); - - /* Mark the edge so it is not deleted */ - - gp_SetVertexVisited(theGraph, ancestor); - gp_SetEdgeVisited(theGraph, gp_GetFirstArc(theGraph, ancestor)); - gp_SetEdgeVisited(theGraph, gp_GetFirstArc(theGraph, descendant)); - gp_SetVertexVisited(theGraph, descendant); - - return OK; -} - -/**************************************************************************** - _AddBackEdge() - - This function transfers the edge records for the edge between the ancestor - and descendant from the forward edge list of the ancestor to the adjacency - lists of the ancestor and descendant. - ****************************************************************************/ - -void _AddBackEdge(graphP theGraph, int ancestor, int descendant) -{ -int fwdArc, backArc; - - /* We get the two edge records of the back edge to embed. */ - - fwdArc = gp_GetVertexFwdArcList(theGraph, ancestor); - while (gp_IsArc(fwdArc)) - { - if (gp_GetNeighbor(theGraph, fwdArc) == descendant) - break; - - fwdArc = gp_GetNextArc(theGraph, fwdArc); - if (fwdArc == gp_GetVertexFwdArcList(theGraph, ancestor)) - fwdArc = NIL; - } - - if (gp_IsNotArc(fwdArc)) - return; - - backArc = gp_GetTwinArc(theGraph, fwdArc); - - /* The forward arc is removed from the fwdArcList of the ancestor. */ - if (gp_GetVertexFwdArcList(theGraph, ancestor) == fwdArc) - { - if (gp_GetNextArc(theGraph, fwdArc) == fwdArc) - gp_SetVertexFwdArcList(theGraph, ancestor, NIL); - else gp_SetVertexFwdArcList(theGraph, ancestor, gp_GetNextArc(theGraph, fwdArc)); - } - - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, fwdArc), gp_GetNextArc(theGraph, fwdArc)); - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, fwdArc), gp_GetPrevArc(theGraph, fwdArc)); - - /* The forward arc is added to the adjacency list of the ancestor. */ - gp_SetPrevArc(theGraph, fwdArc, NIL); - gp_SetNextArc(theGraph, fwdArc, gp_GetFirstArc(theGraph, ancestor)); - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, ancestor), fwdArc); - gp_SetFirstArc(theGraph, ancestor, fwdArc); - - /* The back arc is added to the adjacency list of the descendant. */ - gp_SetPrevArc(theGraph, backArc, NIL); - gp_SetNextArc(theGraph, backArc, gp_GetFirstArc(theGraph, descendant)); - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, descendant), backArc); - gp_SetFirstArc(theGraph, descendant, backArc); - - gp_SetNeighbor(theGraph, backArc, ancestor); -} - -/**************************************************************************** - _DeleteUnmarkedVerticesAndEdges() - - For each vertex, traverse its adjacency list and delete all unvisited edges. - ****************************************************************************/ - -int _DeleteUnmarkedVerticesAndEdges(graphP theGraph) -{ - int v, e; - - /* All of the forward and back arcs of all of the edge records - were removed from the adjacency lists in the planarity algorithm - preprocessing. We now put them back into the adjacency lists - (and we do not mark them), so they can be properly deleted below. */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - while (gp_IsArc(e = gp_GetVertexFwdArcList(theGraph, v))) - _AddBackEdge(theGraph, v, gp_GetNeighbor(theGraph, e)); - } - - /* Now we delete all unmarked edges. We don't delete vertices from the - embedding, but the ones we should delete will become degree zero. */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetEdgeVisited(theGraph, e)) - e = gp_GetNextArc(theGraph, e); - else e = gp_DeleteEdge(theGraph, e, 0); - } - } - - return OK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.c deleted file mode 100644 index dd3ecd0d3..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.c +++ /dev/null @@ -1,280 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif -#include "graph.h" - -/* Imported functions */ - -extern void _ClearVisitedFlags(graphP); - -extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); -extern int _JoinBicomps(graphP theGraph); - -extern int _MarkHighestXYPath(graphP theGraph); - -extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); -extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); -extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); - -extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); - -extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); - -extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -extern int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); -extern int _IsolateOuterplanarityObstructionA(graphP theGraph); -extern int _IsolateOuterplanarityObstructionB(graphP theGraph); - -/* Private function declarations for K_{2,3} searching */ - -int _SearchForK23InBicomp(graphP theGraph, int v, int R); -int _IsolateOuterplanarityObstructionE1orE2(graphP theGraph); -int _IsolateOuterplanarityObstructionE3orE4(graphP theGraph); - -/**************************************************************************** - _SearchForK23InBicomp() - ****************************************************************************/ - -int _SearchForK23InBicomp(graphP theGraph, int v, int R) -{ -isolatorContextP IC = &theGraph->IC; -int X, Y, XPrevLink, YPrevLink; - -/* Begin by determining whether minor A, B or E is detected */ - - if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - -/* Minors A and B result in the desired K_{2,3} homeomorph, - so we isolate it and return NONEMBEDDABLE. */ - - if (theGraph->IC.minorType & (MINORTYPE_A|MINORTYPE_B)) - { - _ClearVisitedFlags(theGraph); - - if (theGraph->IC.minorType & MINORTYPE_A) - { - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - if (_IsolateOuterplanarityObstructionA(theGraph) != OK) - return NOTOK; - } - else if (theGraph->IC.minorType & MINORTYPE_B) - { - int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); - - if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE) - return NOTOK; - - if (_IsolateOuterplanarityObstructionB(theGraph) != OK) - return NOTOK; - } - - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* For minor E (a K_4) , we run the additional tests to see if a K_{2,3} is - entangled with the K_4. If not, then we return OK to indicate that - the outerplanarity embedder should proceed as if the K_4 had not - been found. */ - - /* If any vertices other than R, X, Y and W exist along the - external face, then we can obtain a K_{2,3} by minor E1 or E2 */ - - X = IC->x; - Y = IC->y; - XPrevLink = 1; - YPrevLink = 0; - if (IC->w != _GetNeighborOnExtFace(theGraph, X, &XPrevLink) || - IC->w != _GetNeighborOnExtFace(theGraph, Y, &YPrevLink)) - { - _ClearVisitedFlags(theGraph); - - if (_IsolateOuterplanarityObstructionE1orE2(theGraph) != OK) - return NOTOK; - - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - - /* If X, Y or W make either a direct back edge connection or a - connection through a separated child bicomp to an ancestor of - the current vertex v, then we can obtain a K_{2,3} by minor - E3 or E4. Note that this question is query on X, Y and W is - equivalent to the planarity version of external activity. */ - - gp_UpdateVertexFuturePertinentChild(theGraph, X, v); - gp_UpdateVertexFuturePertinentChild(theGraph, Y, v); - gp_UpdateVertexFuturePertinentChild(theGraph, IC->w, v); - if (FUTUREPERTINENT(theGraph, X, v) || - FUTUREPERTINENT(theGraph, Y, v) || - FUTUREPERTINENT(theGraph, IC->w, v)) - { - _ClearVisitedFlags(theGraph); - - if (_IsolateOuterplanarityObstructionE3orE4(theGraph) != OK) - return NOTOK; - - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* The extra cases for finding a K_{2,3} failed, so the bicomp rooted - by R is a separable subgraph of the input that is isomorphic - to K_4. So, we restore the original vertex orientation of - the bicomp (because it's polite, not because we really have to). - Then, we return OK to tell the outerplanarity embedder that it - can ignore this K_4 and keep processing. */ - - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionE1orE2() - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionE1orE2(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; -int XPrevLink = 1; - - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - -/* Isolate E1 */ - - if (theGraph->IC.px != theGraph->IC.x) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->w) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->py, IC->r) != OK) - return NOTOK; - } - else if (theGraph->IC.py != theGraph->IC.y) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->r) != OK) - return NOTOK; - } - -/* Isolate E2 */ - - else if (IC->w != _GetNeighborOnExtFace(theGraph, IC->x, &XPrevLink)) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->y) != OK) - return NOTOK; - } - - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->r) != OK) - return NOTOK; - } - -/* Final bits are in common */ - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionE3orE4() - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionE3orE4(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; -int u, d, XorY; - - // Minor E3 - gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.x, theGraph->IC.v); - gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.y, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, theGraph->IC.x, theGraph->IC.v) || - FUTUREPERTINENT(theGraph, theGraph->IC.y, theGraph->IC.v)) - { - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - gp_UpdateVertexFuturePertinentChild(theGraph, theGraph->IC.x, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, theGraph->IC.x, theGraph->IC.v)) - XorY = theGraph->IC.x; - else XorY = theGraph->IC.y; - - /* The cases of X future pertinent and Y future pertinent - are the same except for the bicomp external face marking - (because parameter order is important) */ - - if (XorY == theGraph->IC.x) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK) - return NOTOK; - } - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) - return NOTOK; - } - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - if (_FindUnembeddedEdgeToAncestor(theGraph, XorY, &u, &d) != TRUE) - return NOTOK; - - if (theGraph->functions.fpMarkDFSPath(theGraph, u, IC->v) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, XorY, d) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, u, d) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; - } - -/* Otherwise, isolate Minor E4 (reduce to minor A) */ - - if (_FindUnembeddedEdgeToAncestor(theGraph, IC->w, &u, &d) != TRUE) - return NOTOK; - - IC->v = u; - IC->dw = d; - return _IsolateOuterplanarityObstructionA(theGraph); -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.h deleted file mode 100644 index d742299b9..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef GRAPH_K23SEARCH_H -#define GRAPH_K23SEARCH_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graphStructures.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define K23SEARCH_NAME "K23Search" - -int gp_AttachK23Search(graphP theGraph); -int gp_DetachK23Search(graphP theGraph); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.private.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.private.h deleted file mode 100644 index 695997827..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search.private.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef GRAPH_K23SEARCH_PRIVATE_H -#define GRAPH_K23SEARCH_PRIVATE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - // Overloaded function pointers - graphFunctionTable functions; - -} K23SearchContext; - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search_Extensions.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search_Extensions.c deleted file mode 100644 index 5a7c246b6..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK23Search_Extensions.c +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -#include - -#include "graphK23Search.private.h" -#include "graphK23Search.h" - -extern int _SearchForK23InBicomp(graphP theGraph, int v, int R); - -extern int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts); -extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, - int *imageVerts, int maxNumImageVerts); -extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); - -/* Forward declarations of overloading functions */ - -int _K23Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -int _K23Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -int _K23Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); - -/* Forward declarations of functions used by the extension system */ - -void *_K23Search_DupContext(void *pContext, void *theGraph); -void _K23Search_FreeContext(void *); - -/**************************************************************************** - * K23SEARCH_ID - the variable used to hold the integer identifier for this - * extension, enabling this feature's extension context to be distinguished - * from other features' extension contexts that may be attached to a graph. - ****************************************************************************/ - -int K23SEARCH_ID = 0; - -/**************************************************************************** - gp_AttachK23Search() - - This function adjusts the graph data structure to attach the K2,3 search - feature. - ****************************************************************************/ - -int gp_AttachK23Search(graphP theGraph) -{ - K23SearchContext *context = NULL; - - // If the K2,3 search feature has already been attached to the graph - // then there is no need to attach it again - gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); - if (context != NULL) - { - return OK; - } - - // Allocate a new extension context - context = (K23SearchContext *) malloc(sizeof(K23SearchContext)); - if (context == NULL) - { - return NOTOK; - } - - // Put the overload functions into the context function table. - // gp_AddExtension will overload the graph's functions with these, and - // return the base function pointers in the context function table - memset(&context->functions, 0, sizeof(graphFunctionTable)); - - context->functions.fpHandleBlockedBicomp = _K23Search_HandleBlockedBicomp; - context->functions.fpEmbedPostprocess = _K23Search_EmbedPostprocess; - context->functions.fpCheckEmbeddingIntegrity = _K23Search_CheckEmbeddingIntegrity; - context->functions.fpCheckObstructionIntegrity = _K23Search_CheckObstructionIntegrity; - - // Store the K23 search context, including the data structure and the - // function pointers, as an extension of the graph - if (gp_AddExtension(theGraph, &K23SEARCH_ID, (void *) context, - _K23Search_DupContext, _K23Search_FreeContext, - &context->functions) != OK) - { - _K23Search_FreeContext(context); - return NOTOK; - } - - return OK; -} - -/******************************************************************** - gp_DetachK23Search() - ********************************************************************/ - -int gp_DetachK23Search(graphP theGraph) -{ - return gp_RemoveExtension(theGraph, K23SEARCH_ID); -} - -/******************************************************************** - _K23Search_DupContext() - ********************************************************************/ - -void *_K23Search_DupContext(void *pContext, void *theGraph) -{ - K23SearchContext *context = (K23SearchContext *) pContext; - K23SearchContext *newContext = (K23SearchContext *) malloc(sizeof(K23SearchContext)); - - if (newContext != NULL) - { - *newContext = *context; - } - - return newContext; -} - -/******************************************************************** - _K23Search_FreeContext() - ********************************************************************/ - -void _K23Search_FreeContext(void *pContext) -{ - free(pContext); -} - -/******************************************************************** - ********************************************************************/ - -int _K23Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) -{ - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) - { - // If R is the root of a descendant bicomp of v, we push it, but then we know the search for K2,3 - // will be successful and return NONEMBEDDABLE because this condition corresponds to minor A, which - // is a K2,3. Thus, an "OK to proceed with Walkdown searching elsewhere" result cannot happen, - // so we don't have to test for it to detect if we have to pop these two back off the stack. - if (R != RootVertex) - sp_Push2(theGraph->theStack, R, 0); - - // The possible results here are NONEMBEDDABLE if a K2,3 homeomorph is found, or OK if only - // a K4 was found and unblocked such that it is OK for the Walkdown to continue searching - // elsewhere. Note that the OK result can only happen if RootVertex==R since minor E can only - // happen on a child bicomp of vertex v, not a descendant bicomp. - return _SearchForK23InBicomp(theGraph, v, R); - } - - else - { - K23SearchContext *context = NULL; - gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K23Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) -{ - // For K2,3 search, we just return the edge embedding result because the - // search result has been obtained already. - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) - { - return edgeEmbeddingResult; - } - - // When not searching for K2,3, we let the superclass do the work - else - { - K23SearchContext *context = NULL; - gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K23Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) -{ - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) - { - return OK; - } - - // When not searching for K2,3, we let the superclass do the work - else - { - K23SearchContext *context = NULL; - gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K23Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) -{ - // When searching for K2,3, we ensure that theGraph is a subgraph of - // the original graph and that it contains a K2,3 homeomorph - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK23) - { - int degrees[4], imageVerts[5]; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - return NOTOK; - - if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK) - return NOTOK; - - if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE) - { - return OK; - } - - return NOTOK; - } - - // When not searching for K2,3, we let the superclass do the work - else - { - K23SearchContext *context = NULL; - gp_FindExtension(theGraph, K23SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.c deleted file mode 100644 index cd8133c6e..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.c +++ /dev/null @@ -1,2064 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -#include "graphK33Search.h" -#include "graphK33Search.private.h" - -//extern int K33SEARCH_ID; - -#include "graph.h" - -/* Imported functions */ - -//extern void _ClearVisitedFlags(graphP); -extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); -extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); -extern int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue); - -//extern int _GetBicompSize(graphP theGraph, int BicompRoot); -extern int _HideInternalEdges(graphP theGraph, int vertex); -extern int _RestoreInternalEdges(graphP theGraph, int stackBottom); -extern int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot); -extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); -extern int _SetEdgeType(graphP theGraph, int u, int v); - -extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -extern int _JoinBicomps(graphP theGraph); -extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); -extern int _OrientVerticesInEmbedding(graphP theGraph); -//extern void _InvertVertex(graphP theGraph, int V); -extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); -extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); -extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); - -extern int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); -extern int _MarkHighestXYPath(graphP theGraph); - -extern int _IsolateKuratowskiSubgraph(graphP theGraph, int v, int R); - -extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); -extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); -extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); - -extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); - -extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); - -extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -extern int _IsolateMinorE1(graphP theGraph); -//extern int _IsolateMinorE2(graphP theGraph); -extern int _IsolateMinorE3(graphP theGraph); -extern int _IsolateMinorE4(graphP theGraph); - -extern int _MarkDFSPathsToDescendants(graphP theGraph); -extern int _AddAndMarkUnembeddedEdges(graphP theGraph); - -extern void _K33Search_InitEdgeRec(K33SearchContext *context, int e); - -/* Private functions for K_{3,3} searching. */ - -int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R); - -int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context); -int _SearchForMinorE1(graphP theGraph); -int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context); -int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max); -int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex); -int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int vertex, - int closerAncestor, int fartherAncestor); -int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, - int cutVertex, int *pDescendant); -int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker); -int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker); - -int _TestForLowXYPath(graphP theGraph); -int _TestForZtoWPath(graphP theGraph); -int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max); -int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot); -int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink); -int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R); -int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType); -int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType); -int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e); -int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context); - -int _IsolateMinorE5(graphP theGraph); -int _IsolateMinorE6(graphP theGraph, K33SearchContext *context); -int _IsolateMinorE7(graphP theGraph, K33SearchContext *context); - -/**************************************************************************** - _SearchForK33InBicomp() - ****************************************************************************/ - -int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R) -{ -isolatorContextP IC = &theGraph->IC; -int tempResult; - -/* Begin by determining which non-planarity minor is detected */ - - if (_ChooseTypeOfNonplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - -/* If minor A is selected, then the root of the oriented bicomp has been changed */ - - else R = IC->r; - -/* Minors A to D result in the desired K_{3,3} homeomorph, - so we isolate it and return NONEMBEDDABLE. */ - - if (theGraph->IC.minorType & (MINORTYPE_A|MINORTYPE_B|MINORTYPE_C|MINORTYPE_D)) - { - /* First we restore the orientations of the vertices in the - one bicomp we have messed with so that there is no confusion. */ - - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - /* Next we restore the orientation of the embedding so we - can restore the reduced paths (because we avoid modifying - the Kuratowski subgraph isolator to restore reduced paths, - which are a construct of the K_{3,3} search). */ - - if (_OrientVerticesInEmbedding(theGraph) != OK || - _RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - /* Next we simply call the Kuratowski subgraph isolation since - we know now that it will isolate a K_{3,3}. - For minor A, we need to set up the stack that would be - available immediately after a Walkdown failure. */ - - if (theGraph->IC.minorType & MINORTYPE_A) - { - sp_ClearStack(theGraph->theStack); - sp_Push2(theGraph->theStack, R, NIL); - } - - if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* For minor E (a K5 minor), we run the additional tests to see if minors E1 to E4 apply - since these minors isolate a K_{3,3} entangled with the K5. - This is the key location where GetLeastAncestorConnection() must be constant time. */ - - IC->ux = _Fast_GetLeastAncestorConnection(theGraph, context, IC->x); - IC->uy = _Fast_GetLeastAncestorConnection(theGraph, context, IC->y); - IC->uz = _Fast_GetLeastAncestorConnection(theGraph, context, IC->z); - - if (IC->z != IC->w || - IC->uz > MAX(IC->ux, IC->uy) || - (IC->uz < MAX(IC->ux, IC->uy) && IC->ux != IC->uy) || - (IC->x != IC->px || IC->y != IC->py)) - { - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - if (_OrientVerticesInEmbedding(theGraph) != OK || - _RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - if (_IsolateKuratowskiSubgraph(theGraph, v, R) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* If the Kuratowski subgraph isolator will not isolate a K_{3,3} based on minor E, - then a K5 homeomorph could be isolated. However, a K_{3,3} may still be tangled - with the K5, so we now run the additional tests of the K_{3,3} search algorithm. - - If the search finds a K_{3,3} (tempResult of NONEMBEDDABLE), then we remove unwanted - edges from the graph and return NONEMBEDDABLE. If the search has a fault (NOTOK), - then we return. If the result is OK, then a K_{3,3} was not found at this time - and we proceed with some clean-up work below. */ - - if ((tempResult = _RunExtraK33Tests(theGraph, context)) != OK) - { - if (tempResult == NONEMBEDDABLE) - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return tempResult; - } - -/* The extra cases for finding a K_{3,3} did not succeed, so the bicomp rooted by R - is either a K5 homeomorph (with at most a superficially entangled K_{3,3}) or - we have made the special setting that allows us to detect the one "merge blocker" - case that would be too costly to try now. Either way, we can safely reduce the - bicomp to the 4-cycle (R, X, W, Y, R) and proceed with the planarity algorithm. - We also restore the mixed orientation of the bicomp (i.e. the proper - orientation in the context of the edge signs) because this code can work - when ReduceBicomp doesn't do any actual work. */ - - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - if (_ReduceBicomp(theGraph, context, R) != OK) - return NOTOK; - -/* Set visitedInfo values in the bicomp to the initialized state so the planarity - algorithm can properly do the Walkup procedure in future steps */ - - if (_FillVertexVisitedInfoInBicomp(theGraph, IC->r, theGraph->N) != OK) - return NOTOK; - -/* We now intend to ignore the pertinence of W (conceptually eliminating - the connection from W to the current vertex). Note that none of the - bicomp roots in the pertinentRootsList (nor their respective subtrees) - will be visited again by the planarity algorithm because they must've - been only pertinent. If they were future pertinent and pertinent, - then we would've found a K_{3,3} by non-planarity minor B. Thus, the original - Walkup costs that identified the pertinent bicomps we intend to ignore are - one-time costs, preserving linear time. */ - - gp_SetVertexPertinentEdge(theGraph, IC->w, NIL); - gp_SetVertexPertinentRootsList(theGraph, IC->w, NIL); - - return OK; -} - -/**************************************************************************** - _RunExtraK33Tests() - ****************************************************************************/ - -#define USE_MERGEBLOCKER - -int _RunExtraK33Tests(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; -int u_max = MAX3(IC->ux, IC->uy, IC->uz); - -#ifndef USE_MERGEBLOCKER -int u; -#endif - -/* Case 1: If there is a pertinent or future pertinent vertex other than W - on the lower external face path between X and Y (the points of - attachment of the x-y path), then we can isolate a K_{3,3} homeomorph - by Minor E1. */ - - if (_SearchForMinorE1(theGraph) != OK) - return NOTOK; - - if (IC->w != IC->z) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE1(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* Case 2: If W/Z can make an external connection to an ancestor of V - that is descendant to u_{max}, then a K_{3,3} homeomorph can - be isolated with Minor E2. - - OPTIMIZATION: We do not need to check for this case. - We avoid doing so because in very specially crafted cases - it could be too costly if the connection doesn't exist. - However, if the highest numbered ancestor H of the current vertex - that has an external connection from W is a descendant u_{max} - then we will discover a K_{3,3} by Minor A or B in step H - (unless some other test succeeds at finding a K_{3,3} first), - so we just let the non-planarity detector do its work since - Minors A and B both provide a K_{3,3} when found. - - This happens because w is pertinent to H and future pertinent - to u_max or an ancestor of u_max. - - Minor A will happen if, in step H, Walkdown descends to the - bicomp containing the current vertex, x, y and w. Since x - and y would still be future pertinent (they connect to u_max - or higher, i.e. with lesser DFI, than u_max). - - Minor B will happen if the bicomp containing the current vertex, - x, y and w is a descendant of a bicomp that blocks planarity - in step H. The bicomp would be both pertinent (due to w's - connection to H) and future pertinent(due to connections to - ancestors of H by w, x and y). -*/ - -#ifndef USE_MERGEBLOCKER - u = _SearchForDescendantExternalConnection(theGraph, context, IC->w, u_max); - if (u > u_max) - { - IC->uz = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE2(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } -#endif - -/* Case 3: If X or Y can make an external connection to an ancestor of V - that is descendant to u_{max}, then a K_{3,3} homeomorph - can be isolated with Minor E3. - - NOTE: Due to the prior use of the Kuratowski subgraph - isolator, we know that at most one of X, Y or W/Z could have an - external connection to an ancestor of u_{max} = MAX(ux, uy, uz). - - OPTIMIZATION: We do not check for the lower connection required - to find Minor E3 because it might ultimately be too costly. - Instead, we mark the vertex with a 'merge blocker' of u_{max}. - If the planar embedder attempts to merge the vertex prior to step - u_{max}, then the embedder has found the desired connection and a - K_{3,3} homeomorph is isolated at that time. -*/ - -#ifdef USE_MERGEBLOCKER - context->VI[IC->x].mergeBlocker = u_max; -#endif -#ifndef USE_MERGEBLOCKER - u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max); - if (u > u_max) - { - IC->ux = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } -#endif - -#ifdef USE_MERGEBLOCKER - context->VI[IC->y].mergeBlocker = u_max; -#endif -#ifndef USE_MERGEBLOCKER - u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max); - if (u > u_max) - { - IC->uy = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } -#endif - -/* Case 4: If there exists any x-y path with points of attachment px and py - such that px!=x or py!=y, then a K_{3,3} homeomorph can be isolated - with Minor E4. */ - - if (_TestForLowXYPath(theGraph) != OK) - return NOTOK; - - if (IC->px != IC->x || IC->py != IC->y) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE4(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* Case 5: If the x-y path contains an internal vertex that starts a second - internal path from the internal vertex to W/Z, then a K_{3,3} homeomorph - can be isolated with Minor E5. */ - - if (_TestForZtoWPath(theGraph) != OK) - return NOTOK; - - if (gp_GetVertexVisited(theGraph, IC->w)) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE5(theGraph) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - -/* Case 6: If uz < u_{max} and there is an external connection (other than external - connections involving X, Y and W/Z) between an ancestor of u_{max} and a - vertex in the range [V...u_{max}), then a K_{3,3} homeomorph can be - isolated with Minor E6. - - OPTIMIZATION: See _TestForStraddlingBridge() */ - - if (IC->uz < u_max) - { - if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max))) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE6(theGraph, context) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - } - -/* Case 7: If ux < u_{max} or uy < u_{max} and there is an external connection - between an ancestor of u_{max} and a vertex in the range [V...u_{max}) - (except for external connections involving X, Y and W/Z), then a K_{3,3} - homeomorph can be isolated with Minor E7. - - OPTIMIZATION: Same as Case 6.*/ - - if (IC->ux < u_max || IC->uy < u_max) - { - if (gp_IsVertex(_TestForStraddlingBridge(theGraph, context, u_max))) - { - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE7(theGraph, context) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - } - -/* If none of the tests found a K_{3,3}, then we return OK to indicate that nothing - went wrong, but a K_{3,3} was not found. */ - - return OK; -} - -/**************************************************************************** - _SearchForMinorE1() - Search along the external face below the x-y path for a vertex Z other - than W that is future pertinent or pertinent. - ****************************************************************************/ - -int _SearchForMinorE1(graphP theGraph) -{ -int Z=theGraph->IC.px, ZPrevLink=1; - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - - while (Z != theGraph->IC.py) - { - if (Z != theGraph->IC.w) - { - gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) - { - theGraph->IC.z = Z; - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); - return OK; - } - else if (PERTINENT(theGraph, Z)) - { - /* Swap the roles of W and Z */ - - theGraph->IC.z = theGraph->IC.w; - theGraph->IC.w = Z; - - /* If the new W (indicated by Z) was on the path (R, X, old W) then - the new Z (the old W, which has no type mark) is on the path - (X, new W, new Z, Y) so we change the type new Z to being on the - RYW path. Otherwise, the order is (X, new Z, new W, Y), so the - new Z (old W with no type) is type changed to be on the RXW path.*/ - - if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RYW); - else gp_ResetVertexObstructionType(theGraph, theGraph->IC.z, VERTEX_OBSTRUCTIONTYPE_LOW_RXW); - - /* For completeness, we change the new W to type unknown */ - - gp_ClearVertexObstructionType(theGraph, theGraph->IC.w); - - /* The external activity ancestor connection of the new Z must be obtained */ - - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, theGraph->IC.z); - - return OK; - } - } - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - return OK; -} - -/**************************************************************************** - _FinishIsolatorContextInitialization() - Once it has been decided that a desired subgraph can be isolated, it - becomes safe to finish the isolator context initialization. - ****************************************************************************/ - -int _FinishIsolatorContextInitialization(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; - -/* Restore the orientation of the bicomp on which we're working, then - perform orientation of all vertices in graph. (An unnecessary but - polite step that simplifies the description of key states of the - data structures). */ - - if (_OrientVerticesInBicomp(theGraph, IC->r, 1) != OK) - return NOTOK; - - if (_OrientVerticesInEmbedding(theGraph) != OK) - return NOTOK; - -/* Restore any paths that were reduced to single edges */ - - if (_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - -/* We assume that the current bicomp has been marked appropriately, - but we must now clear the visitation flags of all other bicomps. */ - - if (_ClearVisitedFlagsInOtherBicomps(theGraph, IC->r) != OK) - return NOTOK; - -/* To complete the normal behavior of _ClearVisitedFlags() in the - normal isolator context initialization, we also have to clear - the visited flags on all edges that have not yet been embedded */ - - _ClearVisitedFlagsInUnembeddedEdges(theGraph); - -/* Now we can find the descendant ends of unembedded back edges based on - the ancestor settings ux, uy and uz. */ - - if (_FindExternalConnectionDescendantEndpoint(theGraph, IC->ux, IC->x, &IC->dx) != OK || - _FindExternalConnectionDescendantEndpoint(theGraph, IC->uy, IC->y, &IC->dy) != OK || - _FindExternalConnectionDescendantEndpoint(theGraph, IC->uz, IC->z, &IC->dz) != OK) - return NOTOK; - -/* Finally, we obtain the descendant end of an unembedded back edge to - the current vertex. */ - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _Fast_GetLeastAncestorConnection() - - This function searches for an ancestor of the current vertex v adjacent by a - cycle edge to the given cutVertex or one of its DFS descendants appearing in - a separated bicomp. The given cutVertex is assumed to be future pertinent - such that either the leastAncestor or the lowpoint of a separated DFS child - is less than v. We obtain the minimum possible connection from the cutVertex - to an ancestor of v. - - This function performs the same operation as _GetLeastAncestorConnection(), - except in constant time. - ****************************************************************************/ - -int _Fast_GetLeastAncestorConnection(graphP theGraph, K33SearchContext *context, int cutVertex) -{ - int ancestor = gp_GetVertexLeastAncestor(theGraph, cutVertex); - int child = context->VI[cutVertex].separatedDFSChildList; - - if (gp_IsVertex(child) && ancestor > gp_GetVertexLowpoint(theGraph, child)) - ancestor = gp_GetVertexLowpoint(theGraph, child); - - return ancestor; -} - -/**************************************************************************** - _GetAdjacentAncestorInRange() - Returns the ancestor of theVertex that is adjacent to theVertex by an - unembedded back edge and has a DFI strictly between closerAncestor and - fartherAncestor. - Returns NIL if theVertex has no such neighboring ancestor. - ****************************************************************************/ - -int _GetAdjacentAncestorInRange(graphP theGraph, K33SearchContext *context, int theVertex, - int closerAncestor, int fartherAncestor) -{ -int e = context->VI[theVertex].backArcList; - - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) < closerAncestor && - gp_GetNeighbor(theGraph, e) > fartherAncestor) - return gp_GetNeighbor(theGraph, e); - - e = gp_GetNextArc(theGraph, e); - if (e == context->VI[theVertex].backArcList) - e = NIL; - } - return NIL; -} - -/**************************************************************************** - _SearchForDescendantExternalConnection() - Search the cutVertex and each separated child subtree for an external - connection to a vertex ancestor to the current vertex V and descendant to u_max. - - The function returns the descendant of u_max found to have an external - connection to the given cut vertex. - ****************************************************************************/ - -int _SearchForDescendantExternalConnection(graphP theGraph, K33SearchContext *context, int cutVertex, int u_max) -{ -isolatorContextP IC = &theGraph->IC; -int u2 = _GetAdjacentAncestorInRange(theGraph, context, cutVertex, IC->v, u_max); -int child, descendant; - - // Test cutVertex for an external connection to descendant of u_max via direct back edge - if (gp_IsVertex(u2)) - return u2; - - // If there is no direct back edge connection from the cut vertex - // to a vertex on the path between V and u_max, then we will - // look for such a connection in the DFS subtrees rooted by - // separated DFS children of the vertex (ignoring those whose - // lowpoint indicates that they make no external connections) - - // Begin by pushing the separated DFS children of the cut vertex with - // lowpoints indicating connections to ancestors of the current vertex. - sp_ClearStack(theGraph->theStack); - child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex); - while (gp_IsVertex(child)) - { - if (gp_GetVertexLowpoint(theGraph, child) < IC->v && gp_IsSeparatedDFSChild(theGraph, child)) - sp_Push(theGraph->theStack, child); - child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); - } - - // Now process the stack until it is empty or until we've found the desired connection. - while (!sp_IsEmpty(theGraph->theStack)) - { - sp_Pop(theGraph->theStack, descendant); - - // If the vertex has a lowpoint indicating that it makes no external connections, - // then skip the subtree rooted by the vertex - if (gp_GetVertexLowpoint(theGraph, descendant) < IC->v) - { - // Check the subtree root for the desired connection. - u2 = _GetAdjacentAncestorInRange(theGraph, context, descendant, IC->v, u_max); - if (gp_IsVertex(u2)) - return u2; - - // Push each child as a new subtree root to be considered, except skip those whose lowpoint is too great. - child = gp_GetVertexSortedDFSChildList(theGraph, descendant); - while (gp_IsVertex(child)) - { - if (gp_GetVertexLowpoint(theGraph, child) < IC->v) - sp_Push(theGraph->theStack, child); - - child = gp_GetVertexNextDFSChild(theGraph, descendant, child); - } - } - } - - // The only external connections from the cutVertex lead to u_max, so return it. - return u_max; -} - -/**************************************************************************** - _FindExternalConnectionDescendantEndpoint() - - This operation is similar to _FindUnembeddedEdgeToAncestor() except that - we need to be more precise in this case, finding an external connection - from a given cut vertex to a *particular* given ancestor. - - NOTE: By external we don't mean externall active so much as not embedded in - the bicomp containing the cut vertex. - - Returns OK if it finds that either the given cutVertex or one of its - descendants in a separated bicomp has an unembedded back edge - connection to the given ancestor vertex. - Returns NOTOK otherwise (it is an error to not find the descendant because - this function is only called if _SearchForDescendantExternalConnection() - has already determined the existence of the descendant). - ****************************************************************************/ - -int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, - int cutVertex, int *pDescendant) -{ -int child, e; - - // Check whether the cutVertex is directly adjacent to the ancestor - // by an unembedded back edge. - - e = gp_GetVertexFwdArcList(theGraph, ancestor); - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) == cutVertex) - { - *pDescendant = cutVertex; - return OK; - } - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, ancestor)) - e = NIL; - } - - // Now check the descendants of the cut vertex to see if any make - // a connection to the ancestor. - child = gp_GetVertexSortedDFSChildList(theGraph, cutVertex); - while (gp_IsVertex(child)) - { - if (gp_GetVertexLowpoint(theGraph, child) < theGraph->IC.v && gp_IsSeparatedDFSChild(theGraph, child)) - { - if (_FindUnembeddedEdgeToSubtree(theGraph, ancestor, child, pDescendant) == TRUE) - return OK; - } - child = gp_GetVertexNextDFSChild(theGraph, cutVertex, child); - } - - return NOTOK; -} - -/**************************************************************************** - _SearchForMergeBlocker() - - This function helps to implement the merge blocking optimization of - _SearchForDescendantExternalConnection(). The function RunExtraK33Tests() - sets a mergeBlocker rather than run _SearchForDescendantExternalConnection() - in certain cases. This procedure is called by MergeBicomps to test the - embedding stack for a merge blocker before merging any biconnected components. - If a merge blocker is found, then FindK33WithMergeBlocker() is called and - ultimately the embedder's Walkdown function is terminated since a K_{3,3} - is isolated. - - Returns OK on success (whether or not the search found a merge blocker) - NOTOK on internal function failure - pMergeBlocker is set to NIL unless a merge blocker is found. - ****************************************************************************/ - -int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker) -{ -stackP tempStack; -int Z; - -/* Set return result to 'not found' then return if there is no stack to inspect */ - - *pMergeBlocker = NIL; - - if (sp_IsEmpty(theGraph->theStack)) - return OK; - -/* Create a copy of the embedding stack */ - - tempStack = sp_Duplicate(theGraph->theStack); - if (tempStack == NULL) - return NOTOK; - -/* Search the copy of the embedding stack for a merge blocked vertex */ - - while (!sp_IsEmpty(tempStack)) - { - sp_Pop2_Discard(tempStack); /* Move (R, Rout) out of the way */ - sp_Pop2_Discard1(tempStack, Z); /* Get Z, discard ZPrevLink */ - - if (gp_IsVertex(context->VI[Z].mergeBlocker) && - context->VI[Z].mergeBlocker < v) - { - *pMergeBlocker = Z; - break; - } - } - - sp_Free(&tempStack); - return OK; -} - -/**************************************************************************** - _FindK33WithMergeBlocker() - - This function completes the merge blocking optimization by isolating a K_{3,3} - based on minor E3 if a merge blocked vertex was previously found. - - Returns OK on success, NOTOK on internal function failure - ****************************************************************************/ - -int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker) -{ -int R, RPrevLink, u_max, u, e; -isolatorContextP IC = &theGraph->IC; - -/* First, we orient the vertices so we can successfully restore all of the - reduced paths. This needs to be done before reconstructing the context - for CASE 3 of RunExtraK33Tests() because the reconstruction involves - using the Walkup to v from a descendant of v, which will not work if - the descendant is in one of the reduced paths. */ - - if (_OrientVerticesInEmbedding(theGraph) != OK || - _RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - -/* Reconstruct the context that was present for CASE 3 of RunExtraK33Tests() - when we decided to set a mergeBlocker rather than calling - _SearchForDescendantExternalConnection() */ - - /* Obtain the root of the bicomp containing the mergeBlocker. */ - - RPrevLink = 1; - R = mergeBlocker; - while (gp_IsNotVirtualVertex(theGraph, R)) - R = _GetNeighborOnExtFace(theGraph, R, &RPrevLink); - - /* Switch the 'current step' variable v to be equal to the - non-virtual counterpart of the bicomp root. */ - - IC->v = gp_GetPrimaryVertexFromRoot(theGraph, R); - - /* Reinitialize the visitation, pertinence and future pertinence settings from step u_max for step v */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - gp_SetVertexVisitedInfo(theGraph, v, theGraph->N); - gp_SetVertexPertinentEdge(theGraph, v, NIL); - gp_SetVertexPertinentRootsList(theGraph, v, NIL); - - // Any calls to actually determine FUTUREPERTINENT status for a vertex w will actually invoke - // gp_UpdateVertexFuturePertinentChild(theGraph, w, v) beforehand, so only need to reinitialize here - gp_SetVertexFuturePertinentChild(theGraph, v, gp_GetVertexSortedDFSChildList(theGraph, v)); - } - - /* Restore the pertinence settings of step v by doing the Walkup for each - back edge that was not embedded when step v was originally performed. */ - - e = gp_GetVertexFwdArcList(theGraph, IC->v); - while (gp_IsArc(e)) - { - theGraph->functions.fpWalkUp(theGraph, IC->v, e); - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, IC->v)) - e = NIL; - } - -/* Next, we make the standard initialization calls for when we have found - a non-planarity condition. */ - - sp_ClearStack(theGraph->theStack); - - if (_ChooseTypeOfNonplanarityMinor(theGraph, IC->v, R) != OK) - return NOTOK; - - IC->ux = _GetLeastAncestorConnection(theGraph, IC->x); - IC->uy = _GetLeastAncestorConnection(theGraph, IC->y); - IC->uz = _GetLeastAncestorConnection(theGraph, IC->z); - - u_max = MAX3(IC->ux,IC->uy,IC->uz); - -/* Perform the remainder of CASE 3 of RunExtraK33Tests() */ - - if (mergeBlocker == IC->x) - { - u = _SearchForDescendantExternalConnection(theGraph, context, IC->x, u_max); - if (u > u_max) - { - IC->ux = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - } - else return NOTOK; - } - else if (mergeBlocker == IC->y) - { - u = _SearchForDescendantExternalConnection(theGraph, context, IC->y, u_max); - if (u > u_max) - { - IC->uy = u; - if (_FinishIsolatorContextInitialization(theGraph, context) != OK || - _IsolateMinorE3(theGraph) != OK) - return NOTOK; - } - else return NOTOK; - } - else return NOTOK; - -/* Do the final clean-up to obtain the K_{3,3} */ - - if (_DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _TestForLowXYPath() - Is there an x-y path that does not include X? - If not, is there an x-y path that does not include Y? - If not, then we restore the original x-y path. - If such a low x-y path exists, then we adjust px or py accordingly, - and we make sure that X or Y (whichever is excluded) and its edges are - not marked visited. - This method uses the stack, though it is called with an empty stack currently, - it does happen to preserve any preceding stack content. This method pushes - at most one integer per edge incident to the bicomp root plus two integers - per vertex in the bicomp. - ****************************************************************************/ - -int _TestForLowXYPath(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; -int result; -int stackBottom; - -/* Clear the previously marked X-Y path */ - - if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) - return NOTOK; - -/* Save the size of the stack before hiding any edges, so we will know - how many edges to restore */ - - stackBottom = sp_GetCurrentSize(theGraph->theStack); - -/* Hide the internal edges of X */ - - if (_HideInternalEdges(theGraph, IC->x) != OK) - return NOTOK; - -/* Try to find a low X-Y path that excludes X, then restore the - internal edges of X. */ - - result = _MarkHighestXYPath(theGraph); - if (_RestoreInternalEdges(theGraph, stackBottom) != OK) - return NOTOK; - -/* If we found the low X-Y path, then return. */ - - if (result == TRUE) - return OK; - -/* Hide the internal edges of Y */ - - if (_HideInternalEdges(theGraph, IC->y) != OK) - return NOTOK; - -/* Try to find a low X-Y path that excludes Y, then restore the - internal edges of Y. */ - - result = _MarkHighestXYPath(theGraph); - if (_RestoreInternalEdges(theGraph, stackBottom) != OK) - return NOTOK; - -/* If we found the low X-Y path, then return. */ - - if (result == TRUE) - return OK; - -/* Restore the original X-Y path and return with no error - (the search failure is reflected by no change to px and py */ - - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _TestForZtoWPath() - This function tests whether there is a path inside the bicomp leading from W - to some internal node of the x-y path. If there is, the path is marked (the - visited flags of its vertices and edges are set). - - Upon function return, the marking (visited flag setting) of W distinguishes - whether the path was found. - - The function returns NOTOK on internal error, OK otherwise. - - Preconditions: All internal vertices have an obstruction type setting of - unknown, as do W and the bicomp root. There is an X-Y path marked visited. - So, we start a depth first search from W to find a visited vertex, except - we prune the search to ignore vertices whose obstruction type is other than - unknown. This ensures the path found, if any, avoids external face vertices, - including avoiding X and Y. Furthermore, the path search is completed without - traversing to R due to the obstructing X-Y path. - - The depth first search has to "mark" the vertices it has seen as visited, - but the visited flags are already in use to distinguish the X-Y path. - So, we reuse the visitedInfo setting of each vertex. The core planarity - algorithm makes settings between 0 and N, so we will regard all of those - as indicating 'unvisited' by this method, and use -1 to indicate visited. - These markings need not be cleaned up because, if the desired path is found - the a K_{3,3} is isolated and if the desired path is not found then the - bicomp is reduced and the visitedInfo in the remaining vertices are set - appropriately for future Walkup processing of the core planarity algorithm. - - For each vertex we visit, if it is an internal vertex on the X-Y path - (i.e. visited flag set and obstruction type unknown), then we want to stop - and unroll the stack to obtain the desired path (described below). If the - vertex is internal but not on the X-Y path (i.e. visited flag clear and - obstruction type unknown), then we want to visit its neighbors, except - those already marked visited by this method (i.e. those with visitedInfo - of -1) and those with a known obstruction type. - - We want to manage the stack so that it when the desired vertex is found, - the stack contains the desired path. So, we do not simply push all the - neighbors of the vertex being visited. First, given that we have popped - some vertex-edge pair (v, e), we push *only* the next edge after e in - v's adjacency list (starting with the first if e is NIL) that leads to a - new 'eligible' vertex. An eligible vertex is one whose obstruction type - is unknown and whose visitedInfo is other than -1 (so, internal and not - yet processed by this method). Second, when we decide a new vertex w - adjacent to v is eligible, we push not only (v, e) but also (w, NIL). - When we later pop the vertex-edge pair containing NIL, we know that - the vertex obstruction type is unknown so we test whether its visited - flag is set (indicating an internal vertex on the X-Y path). If so, then - we can stop the depth first search, then use the vertices and edges - remaining on the stack to mark the desired path from the external face - vertex W to an internal vertex Z on the X-Y path. - - If we pop (v, NIL) and find that the visited flag of v is clear, then it - is not the desired connection endpoint to the X-Y path. We need to process - all paths extending from it, but we don't want any of those paths to cycle - back to this vertex, so we mark it as ineligible by putting -1 in its - visitedInfo member. This is also the case in which the _first_ edge record e - leading from v to an eligible vertex w is obtained, whereupon we push both - (v, e) and (w, NIL). Eventually all paths leading from w to eligible - vertices will be explored, and if none find the desired vertex connection - to the X-Y path, then (v, e) is popped. Now we search the adjacency list of - v starting after e to find the _next_ edge record that indicates the an - eligible vertex to visit. None of the vertices processed while visiting paths - extending from w will be eligible anymore, so it can be seen that this method - is a depth first search. If no remaining edges from v indicate eligible - vertices, then nothing is pushed and we simply go to the next iteration, - which pops a 2-tuple containing the vertex u and the edge record e that - points to v. Finally, if the stack empties without finding the desired vertex, - then the first loop ends, and the second main loop does not mark a path because - the stack is empty. - ****************************************************************************/ - -int _TestForZtoWPath(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; -int v, e, w; - - sp_ClearStack(theGraph->theStack); - sp_Push2(theGraph->theStack, IC->w, NIL); - - while (!sp_IsEmpty(theGraph->theStack)) - { - sp_Pop2(theGraph->theStack, v, e); - - if (gp_IsNotArc(e)) - { - // If the vertex is visited, then it is a member of the X-Y path - // Because it is being popped, its obstruction type is unknown because - // that is the only kind of vertex pushed. - // Hence, we break because we've found the desired path. - if (gp_GetVertexVisited(theGraph, v)) - break; - - // Mark this vertex as being visited by this method (i.e. ineligible - // to have processing started on it again) - gp_SetVertexVisitedInfo(theGraph, v, -1); - - e = gp_GetFirstArc(theGraph, v); - } - else - e = gp_GetNextArc(theGraph, e); - - // This while loop breaks on the first edge it finds that is eligible to be - // pushed. Once that happens, we break. The successive edges of a vertex are - // only pushed (see the else clause above) once all paths extending from v - // through e have been explored and found not to contain the desired path - while (gp_IsArc(e)) - { - w = gp_GetNeighbor(theGraph, e); - - // The test for w being a virtual vertex is just safeguarding the two subsequent calls, - // but it can never happen due to the obstructing X-Y path. - if (gp_IsNotVirtualVertex(theGraph, w) && - gp_GetVertexVisitedInfo(theGraph, w) != -1 && - gp_GetVertexObstructionType(theGraph, w) == VERTEX_OBSTRUCTIONTYPE_UNKNOWN) - { - sp_Push2(theGraph->theStack, v, e); - sp_Push2(theGraph->theStack, w, NIL); - - break; - } - - e = gp_GetNextArc(theGraph, e); - } - } - - while (!sp_IsEmpty(theGraph->theStack)) - { - sp_Pop2(theGraph->theStack, v, e); - gp_SetVertexVisited(theGraph, v); - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - return OK; -} - -/**************************************************************************** - _TestForStraddlingBridge() - We proceed on the path [V...u_{max}) from the current vertex V up to and - excluding u_{max}. For each vertex p, we test whether p has a least - ancestor less than u_{max} and whether p has a DFS child c that is not an - ancestor of X, Y and W and that has a connection to an ancestor of u_{max} - (in other words, whether the child C has a lowpoint less than u_{max}). - - The sortedDFSChildLIst of the vertex p is scanned for the separated DFS - child c of least lowpoint, excluding the ancestor of X, Y and W. - - If no bridge straddling u_{max} is found, the function returns NIL. - If a straddling bridge is found, the function returns a descendant d - of p in the subtree rooted by c such that d has a leastAncestor less - than u_{max}. Given the vertex d, the path through the straddling - bridge required in Minors E6 and E7 is easy to identify: Mark the - DFS tree path from d to p, and add and mark the edge from d to its - least ancestor. - - OPTIMIZATION: If a straddling bridge is not found, then in each tree edge of - the path [V...u_{max}) we set the member noStraddle equal to u_{max}. - Then, we modify the above stated routine so that if it is testing - for a straddling bridge of u_{max} along this path, it will stop - if it encounters an edge with noStraddle equal to u_{max}. - Also, the optimization will only set noStraddle equal to - u_{max} on the portion of the path that is traversed. Finally, if - noStraddle is set to a value other than NIL, the setting will be - ignored and it will not be changed. - - Due to this optimization, we do not traverse a path more than once - to find out whether a vertex on the path has a bridge that straddles - u_{max}. This leaves two questions: - 1) What if a future step must determine whether there is a - straddling bridge of an ancestor of u_{max}? - 2) What if a future step must determine whether there is a - straddling bridge of a descendant of u_{max}? - - The condition described in the first question cannot occur because it - would imply the ability to detect a straddling bridge now. - The condition described by the second question may occur, but in the - future step, the bicomp now being tested for a K_{3,3} will be part of - a straddling bridge in that future step. Thus, the straddling - bridge query is asked at most twice along any DFS tree path. - ****************************************************************************/ - -int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max) -{ -isolatorContextP IC = &theGraph->IC; -int p, c, d, excludedChild, e; - - p = IC->v; - excludedChild = gp_GetDFSChildFromRoot(theGraph, IC->r); - d = NIL; - - // Starting at V, traverse the ancestor path to u_max looking for a straddling bridge - while (p > u_max) - { - // If we find a direct edge from p to an ancestor of u_max, the break. - if (gp_GetVertexLeastAncestor(theGraph, p) < u_max) - { - d = p; - break; - } - - // Check for a path from p to an ancestor of u_max using the child of p - // with the least Lowpoint, except the child that is an ancestor of X, Y and W. - // It is possible to do this just using the sortedDFSChildList, but no point - // in not using the separatedDFSChildList - /* - { - int c = gp_GetVertexSortedDFSChildList(theGraph, p); - while (gp_IsVertex(c)) - { - if (c != excludedChild && gp_IsSeparatedDFSChild(theGraph, c)) - { - if (gp_GetVertexLowpoint(theGraph, c) < u_max) - break; - } - - c = gp_GetVertexNextDFSChild(theGraph, p, c); - } - } - */ - c = context->VI[p].separatedDFSChildList; - if (c == excludedChild) - c = LCGetNext(context->separatedDFSChildLists, c, c); - - if (gp_IsVertex(c) && gp_GetVertexLowpoint(theGraph, c) < u_max) - { - _FindUnembeddedEdgeToSubtree(theGraph, gp_GetVertexLowpoint(theGraph, c), c, &d); - break; - } - - // Check for noStraddle of u_max, break if found - e = gp_GetFirstArc(theGraph, p); - if (context->E[e].noStraddle == u_max) - break; - - // Go to the next ancestor - excludedChild = p; - p = gp_GetVertexParent(theGraph, p); - } - - // If d is NIL, then no straddling bridge was found, so we do the noStraddle optimization. - if (gp_IsNotVertex(d)) - { - c = IC->v; - while (c != p) - { - e = gp_GetFirstArc(theGraph, c); - if (gp_IsVertex(context->E[e].noStraddle)) - break; - - context->E[e].noStraddle = u_max; - - c = gp_GetVertexParent(theGraph, c); - } - } - - // Return either NIL indicating no bridge straddling u_max or the descendant d - // used to help mark a straddling bridge that was found by this test. - return d; -} - -/**************************************************************************** - _ReduceBicomp() - - We want to reduce the given biconnected component to a 4-cycle plus an - internal edge connecting X and Y. Each edge is to be associated with a - path from the original graph, preserving the depth first search tree - paths that help connect the vertices R, X, Y, and W. If a K_{3,3} is later found, - the paths are restored, but it is necessary to preserve the DFS tree so that - functions like MarkDFSPath() will be able to pass through the restored bicomp. - Also, if a K_{3,3} is later found due to the merge blocker optimization, then the - internal X-Y path may be needed and, once the bicomp reduction is reversed, - a full DFS subtree connecting all vertices in the bicomp will need to be - restored or else functions that traverse the bicomp will not work. - - For example, _FindK33WithMergeBlocker() invokes ChooseTypeOfNonplanarityMinor() - to help reconstruct the context under which the mergeBlocker was set. - ChooseTypeOfNonplanarityMinor() calls _ClearVisitedFlagsInBicomp(), which - depends on the DFS tree. - - NOTE: The following are some general steps taken in this method: - 1) All edges in the bicomp are marked unvisited - 2) selected paths are marked visited - 3) unvisited edges are deleted - 4) the edges of the bicomp are marked unvisited again - 5) the remaining paths of the bicomp are reduced - Some of the edges that get deleted in step 3 above may represent - paths that were reduced in prior embedder iterations. We delete - the reduction edge but not the path it represents. - If a K_{3,3} is ever found, then the edges of these reduced paths - are still in the graph, though not connected to anything important. - The desired K_{3,3} is marked visited, but step 4 above ensures that - these reduction paths are not marked visited. Hence, they will be - deleted when the K_{3,3} is isolated, and this routine does not - need to restore any reduced paths on the edges it deletes. - We also don't (and don't have the time to) restore any reduction - edges along the paths we intend to keep. - ****************************************************************************/ - -int _ReduceBicomp(graphP theGraph, K33SearchContext *context, int R) -{ -isolatorContextP IC = &theGraph->IC; -int min, max, A, A_edge, B, B_edge; -int rxType, xwType, wyType, yrType, xyType; - -/* The vertices in the bicomp need to be oriented so that functions - like MarkPathAlongBicompExtFace() will work. */ - - if (_OrientVerticesInBicomp(theGraph, R, 0) != OK) - return NOTOK; - -/* The reduced edges start with a default type of 'tree' edge. The - tests below, which identify the additional non-tree paths - needed to complete the reduced bicomp, also identify which - reduced edges need to be cycle edges.*/ - - rxType = xwType = wyType = yrType = xyType = EDGE_TYPE_PARENT; - -/* Now we calculate some values that help figure out the shape of the - DFS subtree whose structure will be retained in the bicomp. */ - - min = MIN3(IC->x, IC->y, IC->w); - max = MAX3(IC->x, IC->y, IC->w); - // int mid = MAX3(MIN(IC->x, IC->y), MIN(IC->x, IC->w), MIN(IC->y, IC->w)); - -/* If the order of descendendancy from V goes first to X, then it can - proceed either to W then Y or to Y then W */ - - if (min == IC->x) - { - /* A is a descendant adjacent to the current vertex by a cycle edge - whose DFS tree path to either mid or max is combined with the - cycle edge to form the path that will be reduced to the - external face cycle edge (V, max). */ - - A_edge = gp_GetLastArc(theGraph, IC->r); - A = gp_GetNeighbor(theGraph, A_edge); - yrType = EDGE_TYPE_BACK; - - /* If Y is max, then a path parallel to the X-Y path will be a - second path reduced to a cycle edge. We find the neighbor B - of min=X on the X-Y path. The edge (B, min) is a cycle edge - that, along with the DFS tree path (B, ..., max), will be - retained and reduced to a cycle edge. */ - - if (max == IC->y) - { - B_edge = gp_GetLastArc(theGraph, IC->x); - while (B_edge != gp_GetFirstArc(theGraph, IC->x)) - { - if (gp_GetEdgeVisited(theGraph, B_edge)) break; - B_edge = gp_GetPrevArc(theGraph, B_edge); - } - - if (!gp_GetEdgeVisited(theGraph, B_edge)) - return NOTOK; - - B = gp_GetNeighbor(theGraph, B_edge); - xyType = EDGE_TYPE_BACK; - } - - /* Otherwise, W is max so we find the neighbor B of min=X on the - lower external face path (X, ..., W), which excludes V. The - cycle edge (B, min) and the DFS tree path (B, max) will be - retained and reduced to a cycle edge.*/ - - else if (max == IC->w) - { - B_edge = gp_GetFirstArc(theGraph, IC->x); - B = gp_GetNeighbor(theGraph, B_edge); - xwType = EDGE_TYPE_BACK; - } - - else return NOTOK; - } - -/* Otherwise, the order of descendancy from V goes first to Y, then it - proceeds to either W then X or to X then W. The */ - - else - { - A_edge = gp_GetFirstArc(theGraph, IC->r); - A = gp_GetNeighbor(theGraph, A_edge); - rxType = EDGE_TYPE_BACK; - - if (max == IC->x) - { - B_edge = gp_GetFirstArc(theGraph, IC->y); - while (B_edge != gp_GetLastArc(theGraph, IC->y)) - { - if (gp_GetEdgeVisited(theGraph, B_edge)) break; - B_edge = gp_GetNextArc(theGraph, B_edge); - } - - if (!gp_GetEdgeVisited(theGraph, B_edge)) - return NOTOK; - - B = gp_GetNeighbor(theGraph, B_edge); - xyType = EDGE_TYPE_BACK; - } - - else if (max == IC->w) - { - B_edge = gp_GetLastArc(theGraph, IC->y); - B = gp_GetNeighbor(theGraph, B_edge); - wyType = EDGE_TYPE_BACK; - } - - else return NOTOK; - } - -/* Now that we have collected the information on which cycle edge and - which tree paths will actually be retained, we clear the visited - flags so the current X-Y path will not be retained (an X-Y path - formed mostly or entirely from DFS tree edges is retained). */ - - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - -/* Now we mark the tree path from the maximum numbered vertex up - to the bicomp root. This marks one of the following four paths: - Case 1. (V, ..., X=min, ..., W=mid, ..., Y=max) - Case 2. (V, ..., X=min, ..., Y=mid, ..., W=max) - Case 3. (V, ..., Y=min, ..., W=mid, ..., X=max) - Case 4. (V, ..., Y=min, ..., X=mid, ..., W=max) */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, R, max) != OK) - return NOTOK; - -/* Now we use A to mark a path on the external face corresponding to: - Case 1. (V, ..., Y=max) - Case 2. (V, ..., Y=mid) - Case 3. (V, ..., X=max) - Case 4. (V, ..., X=mid) */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, min==IC->x ? IC->y : IC->x, A) != OK) - return NOTOK; - - gp_SetEdgeVisited(theGraph, A_edge); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, A_edge)); - -/* Now we use B to mark either an X-Y path or a path of the external face - corresponding to: - Case 1. (X=min, ..., B, ..., Y=max) - Case 2. (X=min, ..., B, ..., W=max) - Case 3. (Y=min, ..., B, ..., X=max) - Case 4. (Y=min, ..., B, ..., W=max) */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, max, B) != OK) - return NOTOK; - - gp_SetEdgeVisited(theGraph, B_edge); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, B_edge)); - -/* Delete the unmarked edges in the bicomp. Note that if an unmarked edge - * represents a reduced path, then only the reduction edge is deleted here. - * The path it represents is only deleted later (see NOTE above) */ - - if (_K33Search_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK) - return NOTOK; - -/* Clear all visited flags in the bicomp. - This is the important "step 4" mentioned in the NOTE above */ - - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - -/* Clear all orientation signs in the bicomp. - Note that the whole bicomp may not be properly oriented at this point - because we may have exchanged external face paths for internal - DFS tree paths. However, the reduced bicomp will be properly - oriented, and the paths of degree 2 vertices will have their - orientations fixed if/when reduction edges are restored. */ - - if (_ClearInvertedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - -/* Reduce the paths to single edges. - Note that although the whole bicomp may not be properly oriented at this - point (as noted above), the four principal vertices R, X, W and Y still - are consistently oriented with one another, e.g. R's link[0] indicates - the external face path toward X that excludes W and Y, and X's link[1] - indicates that same path. */ - - if (_ReduceExternalFacePathToEdge(theGraph, context, R, IC->x, rxType) != OK || - _ReduceExternalFacePathToEdge(theGraph, context, IC->x, IC->w, xwType) != OK || - _ReduceExternalFacePathToEdge(theGraph, context, IC->w, IC->y, wyType) != OK || - _ReduceExternalFacePathToEdge(theGraph, context, IC->y, R, yrType) != OK) - return NOTOK; - - if (_ReduceXYPathToEdge(theGraph, context, IC->x, IC->y, xyType) != OK) - return NOTOK; - - return OK; -} - -/******************************************************************** - Edge deletion that occurs during a reduction or restoration of a - reduction is augmented by clearing the K_{3,3} search-specific - data members. This is augmentation is not needed in the delete edge - operations that happen once a K_{3,3} homeomorph has been found and - marked for isolation. - ********************************************************************/ - -int _K33Search_DeleteEdge(graphP theGraph, K33SearchContext *context, int e, int nextLink) -{ - _K33Search_InitEdgeRec(context, e); - _K33Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e)); - - return gp_DeleteEdge(theGraph, e, nextLink); -} - -/******************************************************************** - _K33Search_DeleteUnmarkedEdgesInBicomp() - - This function deletes from a given biconnected component all edges - whose visited member is zero. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls - the overloaded _K33_DeleteEdge() rather than gp_DeleteEdge() - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetEdgeVisited(theGraph, e) - ? gp_GetNextArc(theGraph, e) - : _K33Search_DeleteEdge(theGraph, context, e, 0); - } - } - return OK; -} - -/**************************************************************************** - _ReduceExternalFacePathToEdge() - ****************************************************************************/ - -int _ReduceExternalFacePathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType) -{ -int prevLink, v, w, e; - - /* If the path is a single edge, then no need for a reduction */ - - prevLink = 1; - v = _GetNeighborOnExtFace(theGraph, u, &prevLink); - if (v == x) - { - gp_SetExtFaceVertex(theGraph, u, 0, x); - gp_SetExtFaceVertex(theGraph, x, 1, u); - return OK; - } - - /* We have the endpoints u and x of the path, and we just computed the - first vertex internal to the path and a neighbor of u. Now we - compute the vertex internal to the path and a neighbor of x. */ - - prevLink = 0; - w = _GetNeighborOnExtFace(theGraph, x, &prevLink); - - /* Delete the two edges that connect the path to the bicomp. - If either edge is a reduction edge, then we have to restore - the path it represents. We can only afford to visit the - endpoints of the path. - Note that in the restored path, the edge incident to each - endpoint of the original path is a newly added edge, - not a reduction edge. */ - - e = gp_GetFirstArc(theGraph, u); - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetFirstArc(theGraph, u); - v = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - e = gp_GetLastArc(theGraph, x); - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetLastArc(theGraph, x); - w = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Add the reduction edge, then set its path connectors so the original - path can be recovered and set the edge type so the essential structure - of the DFS tree can be maintained (The 'Do X to Bicomp' functions - and functions like MarkDFSPath(0 depend on this). */ - - gp_AddEdge(theGraph, u, 0, x, 1); - - e = gp_GetFirstArc(theGraph, u); - context->E[e].pathConnector = v; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType)); - - e = gp_GetLastArc(theGraph, x); - context->E[e].pathConnector = w; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType)); - - /* Set the external face info */ - - gp_SetExtFaceVertex(theGraph, u, 0, x); - gp_SetExtFaceVertex(theGraph, x, 1, u); - - return OK; -} - -/**************************************************************************** - _ReduceXYPathToEdge() - ****************************************************************************/ - -int _ReduceXYPathToEdge(graphP theGraph, K33SearchContext *context, int u, int x, int edgeType) -{ -int e, v, w; - - e = gp_GetFirstArc(theGraph, u); - e = gp_GetNextArc(theGraph, e); - v = gp_GetNeighbor(theGraph, e); - - /* If the XY-path is a single edge, then no reduction is needed */ - - if (v == x) - return OK; - - /* Otherwise, remove the two edges that join the XY-path to the bicomp */ - - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetFirstArc(theGraph, u); - e = gp_GetNextArc(theGraph, e); - v = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - e = gp_GetFirstArc(theGraph, x); - e = gp_GetNextArc(theGraph, e); - w = gp_GetNeighbor(theGraph, e); - if (gp_IsVertex(context->E[e].pathConnector)) - { - if (_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - e = gp_GetFirstArc(theGraph, x); - e = gp_GetNextArc(theGraph, e); - w = gp_GetNeighbor(theGraph, e); - } - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Now add a single edge to represent the XY-path */ - gp_InsertEdge(theGraph, u, gp_GetFirstArc(theGraph, u), 0, - x, gp_GetFirstArc(theGraph, x), 0); - - /* Now set up the path connectors so the original XY-path can be recovered if needed. - Also, set the reduction edge's type to preserve the DFS tree structure */ - - e = gp_GetFirstArc(theGraph, u); - e = gp_GetNextArc(theGraph, e); - context->E[e].pathConnector = v; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, u, x, edgeType)); - - e = gp_GetFirstArc(theGraph, x); - e = gp_GetNextArc(theGraph, e); - context->E[e].pathConnector = w; - gp_SetEdgeType(theGraph, e, _ComputeArcType(theGraph, x, u, edgeType)); - - return OK; -} - -/**************************************************************************** - _RestoreReducedPath() - Given an edge record of an edge used to reduce a path, we want to restore - the path in constant time. - The path may contain more reduction edges internally, but we do not - search for and process those since it would violate the constant time - bound required of this function. - return OK on success, NOTOK on failure - ****************************************************************************/ - -int _RestoreReducedPath(graphP theGraph, K33SearchContext *context, int e) -{ -int eTwin, u, v, w, x; -int e0, e1, eTwin0, eTwin1; - - if (gp_IsNotVertex(context->E[e].pathConnector)) - return OK; - - eTwin = gp_GetTwinArc(theGraph, e); - - u = gp_GetNeighbor(theGraph, eTwin); - v = context->E[e].pathConnector; - w = context->E[eTwin].pathConnector; - x = gp_GetNeighbor(theGraph, e); - - /* Get the locations of the edge records between which the new - edge records must be added in order to reconnect the path - parallel to the edge. */ - - e0 = gp_GetNextArc(theGraph, e); - e1 = gp_GetPrevArc(theGraph, e); - eTwin0 = gp_GetNextArc(theGraph, eTwin); - eTwin1 = gp_GetPrevArc(theGraph, eTwin); - - /* We first delete the edge represented by e and eTwin. We do so before - restoring the path to ensure we do not exceed the maximum arc capacity. */ - - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Now we add the two edges to reconnect the reduced path represented - by the edge [e, eTwin]. The edge record in u is added between e0 and e1. - Likewise, the new edge record in x is added between eTwin0 and eTwin1. */ - - if (gp_IsArc(e0)) - { - if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) - return NOTOK; - } - - if (gp_IsArc(eTwin0)) - { - if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) - return NOTOK; - } - - // Set the types of the newly added edges. In both cases, the first of the two - // vertex parameters is known to be degree 2 because they are internal to the - // path being restored, so this operation is constant time. - if (_SetEdgeType(theGraph, v, u) != OK || - _SetEdgeType(theGraph, w, x) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _RestoreAndOrientReducedPaths() - This function searches the embedding for any edges that are specially marked - as being representative of a path that was previously reduced to a - single edge by _ReduceBicomp(). The edge is replaced by the path. - Note that the new path may contain more reduction edges, and these will be - iteratively expanded by the outer for loop. - - If the edge records of an edge being expanded are the first or last arcs - of the edge's vertex endpoints, then the edge may be along the external face. - If so, then the vertices along the path being restored must be given a - consistent orientation with the endpoints. It is expected that the embedding - will have been oriented prior to this operation. - ****************************************************************************/ - -int _RestoreAndOrientReducedPaths(graphP theGraph, K33SearchContext *context) -{ - int EsizeOccupied, e, eTwin, u, v, w, x, visited; - int e0, eTwin0, e1, eTwin1; - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;) - { - if (gp_IsVertex(context->E[e].pathConnector)) - { - visited = gp_GetEdgeVisited(theGraph, e); - - eTwin = gp_GetTwinArc(theGraph, e); - u = gp_GetNeighbor(theGraph, eTwin); - v = context->E[e].pathConnector; - w = context->E[eTwin].pathConnector; - x = gp_GetNeighbor(theGraph, e); - - /* Now we need the predecessor and successor edge records - of e and eTwin. The edge (u, v) will be inserted so - that the record in u's adjacency list that indicates v - will be between e0 and e1. Likewise, the edge record - (x -> w) will be placed between eTwin0 and eTwin1. */ - - e0 = gp_GetNextArc(theGraph, e); - e1 = gp_GetPrevArc(theGraph, e); - eTwin0 = gp_GetNextArc(theGraph, eTwin); - eTwin1 = gp_GetPrevArc(theGraph, eTwin); - - /* We first delete the edge represented by e and eTwin. We do so before - restoring the path to ensure we do not exceed the maximum arc capacity. */ - - _K33Search_DeleteEdge(theGraph, context, e, 0); - - /* Now we add the two edges to reconnect the reduced path represented - by the edge [e, eTwin]. The edge record in u is added between e0 and e1. - Likewise, the new edge record in x is added between eTwin0 and eTwin1. */ - - if (gp_IsArc(e0)) - { - if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) - return NOTOK; - } - - if (gp_IsArc(eTwin0)) - { - if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) - return NOTOK; - } - - /* Set the types of the newly added edges */ - - if (_SetEdgeType(theGraph, u, v) != OK || - _SetEdgeType(theGraph, w, x) != OK) - return NOTOK; - - /* We determine whether the reduction edge may be on the external face, - in which case we will need to ensure that the vertices on the path - being restored are consistently oriented. This will accommodate - future invocations of MarkPathAlongBicompExtFace(). - Note: If e0, e1, eTwin0 or eTwin1 is not an edge, then it is - because we've walked off the end of the edge record list, - which happens when e and eTwin are either the first or - last edge of the containing vertex. In turn, the first - and last edges of a vertex are the ones that hold it onto - the external face, if it is on the external face. */ - - if ((gp_IsNotArc(e0) && gp_IsNotArc(eTwin1)) || (gp_IsNotArc(e1) && gp_IsNotArc(eTwin0))) - { - if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - - /* The internal XY path was already marked as part of the decision logic - that made us decide we could find a K_{3,3} and hence that we should - reverse all of the reductions. Subsequent code counts on the fact - that the X-Y path is already marked, so if we replace a marked edge - with a path, then we need to mark the path. Similarly, for an unmarked - edge, the replacement path should be unmarked. */ - - if (visited) - { - if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - else - { - if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - } - else e+=2; - } - - return OK; -} - -/**************************************************************************** - _MarkStraddlingBridgePath() - ****************************************************************************/ - -int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d) -{ -isolatorContextP IC = &theGraph->IC; -int p, e; - -/* Find the point of intersection p between the path (v ... u_max) - and the path (d ... u_max). */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, u_max, IC->r) != OK) - return NOTOK; - - p = d; - while (!gp_GetVertexVisited(theGraph, p)) - { - gp_SetVertexVisited(theGraph, p); - - e = gp_GetFirstArc(theGraph, p); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - break; - - e = gp_GetNextArc(theGraph, e); - } - - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - - p = gp_GetNeighbor(theGraph, e); - - /* If p is a root copy, mark it visited and skip to the parent copy */ - if (gp_IsVirtualVertex(theGraph, p)) - { - gp_SetVertexVisited(theGraph, p); - p = gp_GetPrimaryVertexFromRoot(theGraph, p); - } - } - -/* Unmark the path (p ... u_max), which was marked to help find p. - The path from v to u_{max} is not needed to form a K_{3,3} except - for the portion of the path up to p that, with the straddling - bridge path, comprises part of the connection to u_d. In the - minor, the path between v and p is edge contracted. */ - - while (p != u_max) - { - e = gp_GetFirstArc(theGraph, p); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - break; - - e = gp_GetNextArc(theGraph, e); - } - - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - - p = gp_GetNeighbor(theGraph, e); - gp_ClearVertexVisited(theGraph, p); - - /* If p is a root copy, clear its visited flag and skip to the - parent copy */ - - if (gp_IsVirtualVertex(theGraph, p)) - { - p = gp_GetPrimaryVertexFromRoot(theGraph, p); - gp_ClearVertexVisited(theGraph, p); - } - } - -/* The straddling bridge must join the path (u_max ... u_min). If u_d is an - ancestor of u_min, then mark the path that joins u_d to u_min. */ - - if (u_d < u_min) - if (theGraph->functions.fpMarkDFSPath(theGraph, u_d, u_min) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE5() - The paths (x, w), (y, w) and (v, u_{max}) are not needed. - The x-y path and the internal w-z path are already marked. - ****************************************************************************/ - -int _IsolateMinorE5(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, MIN3(IC->ux,IC->uy,IC->uz), - MAX3(IC->ux,IC->uy,IC->uz)) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE6() - The paths (x, y), (v, w) and (v, u_{max}) are not needed. - The path through the straddling bridge that connects from an ancestor of - u_{max} to v is required, but it may connnect to an ancestor p of v. - In such a case, the path (v, p) is required, while (p, u_{max}) is not. - ****************************************************************************/ - -int _IsolateMinorE6(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; -int u_min, u_max, d, u_d; - -/* Clear the previously marked x-y path */ - - if (_ClearVisitedFlagsInBicomp(theGraph, IC->r) != OK) - return NOTOK; - -/* Clear dw to stop the marking of path (v, w) */ - - IC->dw = NIL; - -/* Mark (v, ..., x, ..., w, ..., y, ... v) */ - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK) - return NOTOK; - -/* Mark the path through the straddling bridge (except for the final - edge (u_d, d) which is added last by convention). */ - - u_min = MIN3(IC->ux,IC->uy,IC->uz); - u_max = MAX3(IC->ux,IC->uy,IC->uz); - d = _TestForStraddlingBridge(theGraph, context, u_max); - u_d = gp_GetVertexLeastAncestor(theGraph, d); - - if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK) - return NOTOK; - -/* Make the final markings and edge additions */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK || - _AddAndMarkEdge(theGraph, u_d, d) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateMinorE7() - ****************************************************************************/ - -int _IsolateMinorE7(graphP theGraph, K33SearchContext *context) -{ -isolatorContextP IC = &theGraph->IC; -int u_min, u_max, d, u_d; - -/* Mark the appropriate two portions of the external face depending on - symmetry condition */ - - if (IC->uy < IC->ux) - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->x) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->w, IC->y) != OK) - return NOTOK; - } - else - { - if (_MarkPathAlongBicompExtFace(theGraph, IC->x, IC->w) != OK || - _MarkPathAlongBicompExtFace(theGraph, IC->y, IC->r) != OK) - return NOTOK; - } - -/* Mark the path through the straddling bridge (except for the final - edge (u_d, d) which is added last by convention). */ - - u_min = MIN3(IC->ux,IC->uy,IC->uz); - u_max = MAX3(IC->ux,IC->uy,IC->uz); - d = _TestForStraddlingBridge(theGraph, context, u_max); - u_d = gp_GetVertexLeastAncestor(theGraph, d); - - if (_MarkStraddlingBridgePath(theGraph, u_min, u_max, u_d, d) != OK) - return NOTOK; - -/* Make the final markings and edge additions */ - - if (theGraph->functions.fpMarkDFSPath(theGraph, u_min, u_max) != OK || - _MarkDFSPathsToDescendants(theGraph) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkUnembeddedEdges(theGraph) != OK || - _AddAndMarkEdge(theGraph, u_d, d) != OK) - return NOTOK; - - return OK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.h deleted file mode 100644 index 3aa54c82f..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef GRAPH_K33SEARCH_H -#define GRAPH_K33SEARCH_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graphStructures.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define K33SEARCH_NAME "K33Search" - -int gp_AttachK33Search(graphP theGraph); -int gp_DetachK33Search(graphP theGraph); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.private.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.private.h deleted file mode 100644 index ad9769478..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search.private.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef GRAPH_K33SEARCH_PRIVATE_H -#define GRAPH_K33SEARCH_PRIVATE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Additional equipment for each EdgeRec -typedef struct -{ - int noStraddle, pathConnector; -} K33Search_EdgeRec; - -typedef K33Search_EdgeRec * K33Search_EdgeRecP; - -// Additional equipment for each primary vertex -typedef struct -{ - int separatedDFSChildList, backArcList, mergeBlocker; -} K33Search_VertexInfo; - -typedef K33Search_VertexInfo * K33Search_VertexInfoP; - - -typedef struct -{ - // Helps distinguish initialize from re-initialize - int initialized; - - // The graph that this context augments - graphP theGraph; - - // Parallel array for additional edge level equipment - K33Search_EdgeRecP E; - - // Parallel array for additional vertex info level equipment - K33Search_VertexInfoP VI; - - // Storage for the separatedDFSChildLists, and - // to help with linear time sorting of same by lowpoints - listCollectionP separatedDFSChildLists; - int *buckets; - listCollectionP bin; - - // Overloaded function pointers - graphFunctionTable functions; - -} K33SearchContext; - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search_Extensions.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search_Extensions.c deleted file mode 100644 index 60d14ed73..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK33Search_Extensions.c +++ /dev/null @@ -1,762 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif -#include - -#include "graphK33Search.private.h" -#include "graphK33Search.h" - -extern int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker); -extern int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker); -extern int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R); - -extern int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts); -extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, - int *imageVerts, int maxNumImageVerts); -extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); - -/* Forward declarations of local functions */ - -void _K33Search_ClearStructures(K33SearchContext *context); -int _K33Search_CreateStructures(K33SearchContext *context); -int _K33Search_InitStructures(K33SearchContext *context); - -void _K33Search_InitEdgeRec(K33SearchContext *context, int e); -void _K33Search_InitVertexInfo(K33SearchContext *context, int v); - -/* Forward declarations of overloading functions */ - -int _K33Search_EmbeddingInitialize(graphP theGraph); -void _CreateBackArcLists(graphP theGraph, K33SearchContext *context); -void _CreateSeparatedDFSChildLists(graphP theGraph, K33SearchContext *context); -void _K33Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); -int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); -void _K33Search_MergeVertex(graphP theGraph, int W, int WPrevLink, int R); -int _K33Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); - -int _K33Search_InitGraph(graphP theGraph, int N); -void _K33Search_ReinitializeGraph(graphP theGraph); -int _K33Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -/* Forward declarations of functions used by the extension system */ - -void *_K33Search_DupContext(void *pContext, void *theGraph); -void _K33Search_FreeContext(void *); - -/**************************************************************************** - * K33SEARCH_ID - the variable used to hold the integer identifier for this - * extension, enabling this feature's extension context to be distinguished - * from other features' extension contexts that may be attached to a graph. - ****************************************************************************/ - -int K33SEARCH_ID = 0; - -/**************************************************************************** - gp_AttachK33Search() - - This function adjusts the graph data structure to attach the K3,3 search - feature. - ****************************************************************************/ - -int gp_AttachK33Search(graphP theGraph) -{ - K33SearchContext *context = NULL; - - // If the K3,3 search feature has already been attached to the graph, - // then there is no need to attach it again - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - if (context != NULL) - { - return OK; - } - - // Allocate a new extension context - context = (K33SearchContext *) malloc(sizeof(K33SearchContext)); - if (context == NULL) - { - return NOTOK; - } - - // First, tell the context that it is not initialized - context->initialized = 0; - - // Save a pointer to theGraph in the context - context->theGraph = theGraph; - - // Put the overload functions into the context function table. - // gp_AddExtension will overload the graph's functions with these, and - // return the base function pointers in the context function table - memset(&context->functions, 0, sizeof(graphFunctionTable)); - - context->functions.fpEmbeddingInitialize = _K33Search_EmbeddingInitialize; - context->functions.fpEmbedBackEdgeToDescendant = _K33Search_EmbedBackEdgeToDescendant; - context->functions.fpMergeBicomps = _K33Search_MergeBicomps; - context->functions.fpMergeVertex = _K33Search_MergeVertex; - context->functions.fpHandleBlockedBicomp = _K33Search_HandleBlockedBicomp; - context->functions.fpEmbedPostprocess = _K33Search_EmbedPostprocess; - context->functions.fpCheckEmbeddingIntegrity = _K33Search_CheckEmbeddingIntegrity; - context->functions.fpCheckObstructionIntegrity = _K33Search_CheckObstructionIntegrity; - - context->functions.fpInitGraph = _K33Search_InitGraph; - context->functions.fpReinitializeGraph = _K33Search_ReinitializeGraph; - context->functions.fpEnsureArcCapacity = _K33Search_EnsureArcCapacity; - - _K33Search_ClearStructures(context); - - // Store the K33 search context, including the data structure and the - // function pointers, as an extension of the graph - if (gp_AddExtension(theGraph, &K33SEARCH_ID, (void *) context, - _K33Search_DupContext, _K33Search_FreeContext, - &context->functions) != OK) - { - _K33Search_FreeContext(context); - return NOTOK; - } - - // Create the K33-specific structures if the size of the graph is known - // Attach functions are always invoked after gp_New(), but if a graph - // extension must be attached before gp_Read(), then the attachment - // also happens before gp_InitGraph(), which means N==0. - // However, sometimes a feature is attached after gp_InitGraph(), in - // which case N > 0 - if (theGraph->N > 0) - { - if (_K33Search_CreateStructures(context) != OK || - _K33Search_InitStructures(context) != OK) - { - _K33Search_FreeContext(context); - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - gp_DetachK33Search() - ********************************************************************/ - -int gp_DetachK33Search(graphP theGraph) -{ - return gp_RemoveExtension(theGraph, K33SEARCH_ID); -} - -/******************************************************************** - _K33Search_ClearStructures() - ********************************************************************/ - -void _K33Search_ClearStructures(K33SearchContext *context) -{ - if (!context->initialized) - { - // Before initialization, the pointers are stray, not NULL - // Once NULL or allocated, free() or LCFree() can do the job - context->E = NULL; - context->VI = NULL; - - context->separatedDFSChildLists = NULL; - context->buckets = NULL; - context->bin = NULL; - - context->initialized = 1; - } - else - { - if (context->E != NULL) - { - free(context->E); - context->E = NULL; - } - if (context->VI != NULL) - { - free(context->VI); - context->VI = NULL; - } - - LCFree(&context->separatedDFSChildLists); - if (context->buckets != NULL) - { - free(context->buckets); - context->buckets = NULL; - } - LCFree(&context->bin); - } -} - -/******************************************************************** - _K33Search_CreateStructures() - Create uninitialized structures for the vertex and edge - levels, and initialized structures for the graph level - ********************************************************************/ -int _K33Search_CreateStructures(K33SearchContext *context) -{ - int VIsize = gp_PrimaryVertexIndexBound(context->theGraph); - int Esize = gp_EdgeIndexBound(context->theGraph); - - if (context->theGraph->N <= 0) - return NOTOK; - - if ((context->E = (K33Search_EdgeRecP) malloc(Esize*sizeof(K33Search_EdgeRec))) == NULL || - (context->VI = (K33Search_VertexInfoP) malloc(VIsize*sizeof(K33Search_VertexInfo))) == NULL || - (context->separatedDFSChildLists = LCNew(VIsize)) == NULL || - (context->buckets = (int *) malloc(VIsize * sizeof(int))) == NULL || - (context->bin = LCNew(VIsize)) == NULL - ) - { - return NOTOK; - } - - return OK; -} - -/******************************************************************** - _K33Search_InitStructures() - ********************************************************************/ -int _K33Search_InitStructures(K33SearchContext *context) -{ -#if NIL == 0 || NIL == -1 - memset(context->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(context->theGraph) * sizeof(K33Search_VertexInfo)); - memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K33Search_EdgeRec)); -#else - graphP theGraph = context->theGraph; - int v, e, Esize; - - if (theGraph->N <= 0) - return OK; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - _K33Search_InitVertexInfo(context, v); - - Esize = gp_EdgeIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - _K33Search_InitEdgeRec(context, e); -#endif - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -int _K33Search_InitGraph(graphP theGraph, int N) -{ - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context == NULL) { - return NOTOK; - } - - theGraph->N = N; - theGraph->NV = N; - if (theGraph->arcCapacity == 0) - theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N; - - if (_K33Search_CreateStructures(context) != OK || - _K33Search_InitStructures(context) != OK) - return NOTOK; - - context->functions.fpInitGraph(theGraph, N); - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -void _K33Search_ReinitializeGraph(graphP theGraph) -{ - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - // Reinitialize the graph - context->functions.fpReinitializeGraph(theGraph); - - // Do the reinitialization that is specific to this module - _K33Search_InitStructures(context); - LCReset(context->separatedDFSChildLists); - LCReset(context->bin); - } -} - -/******************************************************************** - The current implementation does not support an increase of arc - (edge record) capacity once the extension is attached to the graph - data structure. This is only due to not being necessary to support. - For now, it is easy to ensure the correct capacity before attaching - the extension, but support could be added later if there is some - reason to do so. - ********************************************************************/ - -int _K33Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ - return NOTOK; -} - -/******************************************************************** - _K33Search_DupContext() - ********************************************************************/ - -void *_K33Search_DupContext(void *pContext, void *theGraph) -{ - K33SearchContext *context = (K33SearchContext *) pContext; - K33SearchContext *newContext = (K33SearchContext *) malloc(sizeof(K33SearchContext)); - - if (newContext != NULL) - { - int VIsize = gp_PrimaryVertexIndexBound((graphP) theGraph); - int Esize = gp_EdgeIndexBound((graphP) theGraph); - - *newContext = *context; - - newContext->theGraph = (graphP) theGraph; - - newContext->initialized = 0; - _K33Search_ClearStructures(newContext); - if (((graphP) theGraph)->N > 0) - { - if (_K33Search_CreateStructures(newContext) != OK) - { - _K33Search_FreeContext(newContext); - return NULL; - } - - memcpy(newContext->E, context->E, Esize*sizeof(K33Search_EdgeRec)); - memcpy(newContext->VI, context->VI, VIsize*sizeof(K33Search_VertexInfo)); - LCCopy(newContext->separatedDFSChildLists, context->separatedDFSChildLists); - } - } - - return newContext; -} - -/******************************************************************** - _K33Search_FreeContext() - ********************************************************************/ - -void _K33Search_FreeContext(void *pContext) -{ - K33SearchContext *context = (K33SearchContext *) pContext; - - _K33Search_ClearStructures(context); - free(pContext); -} - -/******************************************************************** - _K33Search_EmbeddingInitialize() - - This method overloads the embedding initialization phase of the - core planarity algorithm to provide post-processing that creates - the back arcs list and separated DFS child list (sorted by - lowpoint) for each vertex. - ********************************************************************/ - -int _K33Search_EmbeddingInitialize(graphP theGraph) -{ - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - if (context->functions.fpEmbeddingInitialize(theGraph) != OK) - return NOTOK; - - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - _CreateBackArcLists(theGraph, context); - _CreateSeparatedDFSChildLists(theGraph, context); - } - - return OK; - } - - return NOTOK; -} - -/******************************************************************** - _CreateBackArcLists() - ********************************************************************/ -void _CreateBackArcLists(graphP theGraph, K33SearchContext *context) -{ - int v, e, eTwin, ancestor; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - e = gp_GetVertexFwdArcList(theGraph, v); - while (gp_IsArc(e)) - { - // Get the ancestor endpoint and the associated back arc - ancestor = gp_GetNeighbor(theGraph, e); - eTwin = gp_GetTwinArc(theGraph, e); - - // Put it into the back arc list of the ancestor - if (gp_IsNotArc(context->VI[ancestor].backArcList)) - { - context->VI[ancestor].backArcList = eTwin; - gp_SetPrevArc(theGraph, eTwin, eTwin); - gp_SetNextArc(theGraph, eTwin, eTwin); - } - else - { - int eHead = context->VI[ancestor].backArcList; - int eTail = gp_GetPrevArc(theGraph, eHead); - gp_SetPrevArc(theGraph, eTwin, eTail); - gp_SetNextArc(theGraph, eTwin, eHead); - gp_SetPrevArc(theGraph, eHead, eTwin); - gp_SetNextArc(theGraph, eTail, eTwin); - } - - // Advance to the next forward edge - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, v)) - e = NIL; - } - } -} - -/******************************************************************** - _CreateSeparatedDFSChildLists() - - Each vertex gets a list of its DFS children, sorted by lowpoint. - ********************************************************************/ - -void _CreateSeparatedDFSChildLists(graphP theGraph, K33SearchContext *context) -{ -int *buckets; -listCollectionP bin; -int v, L, DFSParent, theList; - - buckets = context->buckets; - bin = context->bin; - - // Initialize the bin and all the buckets to be empty - LCReset(bin); - for (L = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, L); L++) - buckets[L] = NIL; - - // For each vertex, add it to the bucket whose index is equal to the lowpoint of the vertex. - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - L = gp_GetVertexLowpoint(theGraph, v); - buckets[L] = LCAppend(bin, buckets[L], v); - } - - // For each bucket, add each vertex in the bucket to the separatedDFSChildList of its DFSParent. - // Since lower numbered buckets are processed before higher numbered buckets, vertices with lower - // lowpoint values are added before those with higher lowpoint values, so the separatedDFSChildList - // of each vertex is sorted by lowpoint - for (L = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, L); L++) - { - v = buckets[L]; - - // Loop through all the vertices with lowpoint L, putting each in the list of its parent - while (gp_IsVertex(v)) - { - DFSParent = gp_GetVertexParent(theGraph, v); - - if (gp_IsVertex(DFSParent) && DFSParent != v) - { - theList = context->VI[DFSParent].separatedDFSChildList; - theList = LCAppend(context->separatedDFSChildLists, theList, v); - context->VI[DFSParent].separatedDFSChildList = theList; - } - - v = LCGetNext(bin, buckets[L], v); - } - } -} - -/******************************************************************** - _K33Search_EmbedBackEdgeToDescendant() - - The forward and back arcs of the cycle edge are embedded by the planarity - version of this function. - However, for K_{3,3} subgraph homeomorphism, we also maintain the - list of unembedded back arcs, so we need to remove the back arc from - that list since it is now being put back into the adjacency list. - ********************************************************************/ - -void _K33Search_EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink) -{ - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - // K33 search may have been attached, but not enabled - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - // Get the fwdArc from the adjacentTo field, and use it to get the backArc - int backArc = gp_GetTwinArc(theGraph, gp_GetVertexPertinentEdge(theGraph, W)); - - // Remove the backArc from the backArcList - if (context->VI[W].backArcList == backArc) - { - if (gp_GetNextArc(theGraph, backArc) == backArc) - context->VI[W].backArcList = NIL; - else context->VI[W].backArcList = gp_GetNextArc(theGraph, backArc); - } - - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, backArc), gp_GetNextArc(theGraph, backArc)); - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, backArc), gp_GetPrevArc(theGraph, backArc)); - } - - // Invoke the superclass version of the function - context->functions.fpEmbedBackEdgeToDescendant(theGraph, RootSide, RootVertex, W, WPrevLink); - } -} - -/******************************************************************** - This override of _MergeBicomps() detects a special merge block - that indicates a K3,3 can be found. The merge blocker is an - optimization needed for one case for which detecting a K3,3 - could not be done in linear time by direct searching of a - path of ancestors that is naturally explored eventually by - the core planarity algorithm. - - Returns OK for a successful merge, NOTOK on an internal failure, - or NONEMBEDDABLE if the merge was blocked, in which case - a K_{3,3} homeomorph was isolated. - ********************************************************************/ - -int _K33Search_MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink) -{ - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - /* If the merge is blocked, then a K_{3,3} homeomorph is isolated, - and NONEMBEDDABLE is returned so that the Walkdown terminates */ - - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - int mergeBlocker; - - // We want to test all merge points on the stack - // as well as W, since the connection will go - // from W. So we push W as a 'degenerate' merge point. - sp_Push2(theGraph->theStack, W, WPrevLink); - sp_Push2(theGraph->theStack, NIL, NIL); - - if (_SearchForMergeBlocker(theGraph, context, v, &mergeBlocker) != OK) - return NOTOK; - - if (gp_IsVertex(mergeBlocker)) - { - if (_FindK33WithMergeBlocker(theGraph, context, v, mergeBlocker) != OK) - return NOTOK; - - return NONEMBEDDABLE; - } - - // If no merge blocker was found, then remove W from the stack. - sp_Pop2(theGraph->theStack, W, WPrevLink); - sp_Pop2(theGraph->theStack, W, WPrevLink); - } - - // If the merge was not blocked, then we perform the merge - // When not doing a K3,3 search, then the merge is not - // blocked as far as the K3,3 search method is concerned - // Another algorithms could overload MergeBicomps and block - // merges under certain conditions, but those would be based - // on data maintained by the extension that implements the - // other algorithm-- if *that* algorithm is the one being run - return context->functions.fpMergeBicomps(theGraph, v, RootVertex, W, WPrevLink); - } - - return NOTOK; -} - -/******************************************************************** - _K33Search_MergeVertex() - - Overload of merge vertex that does basic behavior but also removes - the DFS child associated with R from the separatedDFSChildList of W. - ********************************************************************/ -void _K33Search_MergeVertex(graphP theGraph, int W, int WPrevLink, int R) -{ - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - int theList = context->VI[W].separatedDFSChildList; - theList = LCDelete(context->separatedDFSChildLists, theList, gp_GetDFSChildFromRoot(theGraph, R)); - context->VI[W].separatedDFSChildList = theList; - } - - context->functions.fpMergeVertex(theGraph, W, WPrevLink, R); - } -} - -/******************************************************************** - ********************************************************************/ - -void _K33Search_InitEdgeRec(K33SearchContext *context, int e) -{ - context->E[e].noStraddle = NIL; - context->E[e].pathConnector = NIL; -} - -/******************************************************************** - ********************************************************************/ - -void _K33Search_InitVertexInfo(K33SearchContext *context, int v) -{ - context->VI[v].separatedDFSChildList = NIL; - context->VI[v].backArcList = NIL; - context->VI[v].mergeBlocker = NIL; -} - -/******************************************************************** - ********************************************************************/ - -int _K33Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) -{ - K33SearchContext *context = NULL; - - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - if (context == NULL) - return NOTOK; - - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - // If R is the root of a descendant bicomp of v, we push it, but then we know the search for K3,3 - // will be successful and return NONEMBEDDABLE because this condition corresponds to minor A, which - // is a K3,3. Thus, an "OK to proceed with Walkdown searching elsewhere" result cannot happen, - // so we don't have to test for it to detect if we have to pop these two back off the stack. - if (R != RootVertex) - sp_Push2(theGraph->theStack, R, 0); - - // The possible results here are NONEMBEDDABLE if a K3,3 homeomorph is found, or OK if only - // a K5 was found and unblocked such that it is OK for the Walkdown to continue searching - // elsewhere. Note that the OK result can only happen if RootVertex==R since minor E can only - // happen on a child bicomp of vertex v, not a descendant bicomp. - return _SearchForK33InBicomp(theGraph, context, v, RootVertex); - } - else - { - return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K33Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) -{ - // For K3,3 search, we just return the edge embedding result because the - // search result has been obtained already. - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - return edgeEmbeddingResult; - } - - // When not searching for K3,3, we let the superclass do the work - else - { - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K33Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) -{ - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - return OK; - } - - // When not searching for K3,3, we let the superclass do the work - else - { - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K33Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) -{ - // When searching for K3,3, we ensure that theGraph is a subgraph of - // the original graph and that it contains a K3,3 homeomorph - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK33) - { - int degrees[5], imageVerts[6]; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - { - return NOTOK; - } - - if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK) - { - return NOTOK; - } - - if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE) - { - return OK; - } - - return NOTOK; - } - - // When not searching for K3,3, we let the superclass do the work - else - { - K33SearchContext *context = NULL; - gp_FindExtension(theGraph, K33SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.c deleted file mode 100644 index 5179df39b..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.c +++ /dev/null @@ -1,1490 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif -#include "graphK4Search.h" -#include "graphK4Search.private.h" - -extern int K4SEARCH_ID; - -#include "graph.h" - -/* Imported functions */ - -extern void _InitIsolatorContext(graphP theGraph); -extern void _ClearVisitedFlags(graphP); -extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -//extern int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); -//extern void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); -extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); -//extern int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot); -extern int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); -extern int _SetEdgeType(graphP theGraph, int u, int v); - -extern int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -extern int _JoinBicomps(graphP theGraph); -//extern void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY); -extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); -extern int _OrientVerticesInEmbedding(graphP theGraph); -//extern void _InvertVertex(graphP theGraph, int V); -extern int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); -extern int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); -extern int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); - -extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); -extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); -extern int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); - -extern int _SetVertexTypesForMarkingXYPath(graphP theGraph); -extern int _MarkHighestXYPath(graphP theGraph); -extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); -extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); -extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -extern int _IsolateOuterplanarityObstructionA(graphP theGraph); -//extern int _IsolateOuterplanarityObstructionB(graphP theGraph); -extern int _IsolateOuterplanarityObstructionE(graphP theGraph); - -extern void _K4Search_InitEdgeRec(K4SearchContext *context, int e); - - -/* Private functions for K4 searching (exposed to the extension). */ - -int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R); - -/* Private functions for K4 searching. */ - -int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); - -int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph); -int _K4_FindPlanarityActiveVertex(graphP theGraph, int v, int R, int prevLink, int *pW); -int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY); -void _K4_MarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A); -void _K4_UnmarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A); - -int _K4_IsolateMinorA1(graphP theGraph); -int _K4_IsolateMinorA2(graphP theGraph); -int _K4_IsolateMinorB1(graphP theGraph); -int _K4_IsolateMinorB2(graphP theGraph); - -int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W); -int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A); -int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A); - -int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant); -int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A); -void _K4_ClearVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A); -int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A); -int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, int BicompRoot); - -int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e); -int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context); - -//int _MarkEdge(graphP theGraph, int x, int y); - -/**************************************************************************** - _SearchForK4InBicomp() - ****************************************************************************/ - -int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R) -{ -isolatorContextP IC = &theGraph->IC; - - if (context == NULL) - { - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - if (context == NULL) - return NOTOK; - } - - // Begin by determining whether minor A, B or E is detected - if (_K4_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - - // Minor A indicates the existence of K_{2,3} homeomorphs, but - // we run additional tests to see whether we can either find an - // entwined K4 homeomorph or reduce the bicomp so that the WalkDown - // is enabled to continue to resolve pertinence - if (theGraph->IC.minorType & MINORTYPE_A) - { - // Now that we know we have minor A, we can afford to orient the - // bicomp because we will either find the desired K4 or we will - // reduce the bicomp to an edge. The tests for A1 and A2 are easier - // to implement on an oriented bicomp. - // NOTE: We're in the midst of the WalkDown, so the stack may be - // non-empty, and it has to be preserved with constant cost. - // The stack will have at most 4 integers per cut vertex - // merge point, and this operation will push at most two - // integers per tree edge in the bicomp, so the stack - // will not overflow. - if (sp_GetCapacity(theGraph->theStack) < 6*theGraph->N) - return NOTOK; - - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) - return NOTOK; - - // Case A1: Test whether there is an active vertex Z other than W - // along the external face path [X, ..., W, ..., Y] - if (_K4_FindSecondActiveVertexOnLowExtFacePath(theGraph) == TRUE) - { - // Now that we know we can find a K4, the Walkdown will not continue - // and we can do away with the stack content. - sp_ClearStack(theGraph->theStack); - - // Restore the orientations of the vertices in the bicomp, then orient - // the whole embedding, so we can restore and orient the reduced paths - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK || - _OrientVerticesInEmbedding(theGraph) != OK || - _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - // Set up to isolate K4 homeomorph - _ClearVisitedFlags(theGraph); - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - if (IC->uz < IC->v) - { - if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE) - return NOTOK; - } - else - { - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->z, &IC->dz) != TRUE) - return NOTOK; - } - - // Isolate the K4 homeomorph - if (_K4_IsolateMinorA1(theGraph) != OK || - _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - // Indicate success by returning NONEMBEDDABLE - return NONEMBEDDABLE; - } - - // Case A2: Test whether the bicomp has an XY path - // NOTE: As mentioned above, the stack is also preserved here. - // It will have at most 4 integers per cut vertex merge point, - // and this operation will push at most one integer per tree - // edge in the bicomp, so the stack will not overflow. - if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) - return NOTOK; - - // Marking the X-Y path relies on the bicomp visited flags being cleared - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - - // NOTE: This call preserves the stack and does not overflow. There - // are at most 4 integers per cut vertex merge point, all of which - // are not in the bicomp, and this call pushes at most 3 integers - // per bicomp vertex, so the maximum stack requirement is 4N - if (_MarkHighestXYPath(theGraph) == TRUE) - { - // Now that we know we can find a K4, the Walkdown will not continue - // and we can do away with the stack content. - sp_ClearStack(theGraph->theStack); - - // Restore the orientations of the vertices in the bicomp, then orient - // the whole embedding, so we can restore and orient the reduced paths - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK || - _OrientVerticesInEmbedding(theGraph) != OK || - _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - // Set up to isolate K4 homeomorph - _ClearVisitedFlags(theGraph); - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) { - return NOTOK; - } - - // Isolate the K4 homeomorph - if (_MarkHighestXYPath(theGraph) != TRUE || - _K4_IsolateMinorA2(theGraph) != OK || - _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - // Indicate success by returning NONEMBEDDABLE - return NONEMBEDDABLE; - } - - // else if there was no X-Y path, then we restore the vertex types to - // unknown (though it would suffice to do it just to R and W) - if (_ClearVertexTypeInBicomp(theGraph, R) != OK) - return NOTOK; - - // Since neither A1 nor A2 is found, then we reduce the bicomp to the - // tree edge (R, W). - // NOTE: The visited flags for R and W are restored to values appropriate - // for continuing with future embedding steps - // NOTE: This method invokes several routines that use the stack, but - // all of them preserve the stack and each pushes at most one - // integer per bicomp vertex and pops all of them before returning. - // Again, this means the stack will not overflow. - if (_K4_ReduceBicompToEdge(theGraph, context, R, IC->w) != OK) - return NOTOK; - - // Return OK so that the WalkDown can continue resolving the pertinence of v. - return OK; - } - - // Minor B also indicates the existence of K_{2,3} homeomorphs, but - // we run additional tests to see whether we can either find an - // entwined K4 homeomorph or reduce a portion of the bicomp so that - // the WalkDown can be reinvoked on the bicomp - else if (theGraph->IC.minorType & MINORTYPE_B) - { - int a_x, a_y; - - // Reality check on stack state - if (sp_NonEmpty(theGraph->theStack)) - return NOTOK; - - // Find the vertices a_x and a_y that are active (pertinent or future pertinent) - // and also first along the external face paths emanating from the bicomp root - if (_K4_FindPlanarityActiveVertex(theGraph, v, R, 1, &a_x) != OK || - _K4_FindPlanarityActiveVertex(theGraph, v, R, 0, &a_y) != OK) - return NOTOK; - - // Case B1: If both a_x and a_y are future pertinent, then we can stop and - // isolate a subgraph homeomorphic to K4. - gp_UpdateVertexFuturePertinentChild(theGraph, a_x, v); - gp_UpdateVertexFuturePertinentChild(theGraph, a_y, v); - if (a_x != a_y && FUTUREPERTINENT(theGraph, a_x, v) && FUTUREPERTINENT(theGraph, a_y, v)) - { - if (_OrientVerticesInEmbedding(theGraph) != OK || - _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - // Set up to isolate K4 homeomorph - _ClearVisitedFlags(theGraph); - - IC->x = a_x; - IC->y = a_y; - - if (_FindUnembeddedEdgeToAncestor(theGraph, IC->x, &IC->ux, &IC->dx) != TRUE || - _FindUnembeddedEdgeToAncestor(theGraph, IC->y, &IC->uy, &IC->dy) != TRUE) - return NOTOK; - - // Isolate the K4 homeomorph - if (_K4_IsolateMinorB1(theGraph) != OK || - _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - // Indicate success by returning NONEMBEDDABLE - return NONEMBEDDABLE; - } - - // Reality check: The bicomp with root R is pertinent, and the only - // pertinent or future pertinent vertex on the external face is a_x, - // so it must also be pertinent. - if (a_x == a_y && !PERTINENT(theGraph, a_x)) - return NOTOK; - - // Case B2: Determine whether there is an internal separating X-Y path for a_x or for a_y - // The method makes appropriate isolator context settings if the separator edge is found - if (_K4_FindSeparatingInternalEdge(theGraph, R, 1, a_x, &IC->w, &IC->px, &IC->py) == TRUE || - _K4_FindSeparatingInternalEdge(theGraph, R, 0, a_y, &IC->w, &IC->py, &IC->px) == TRUE) - { - if (_OrientVerticesInEmbedding(theGraph) != OK || - _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - // Set up to isolate K4 homeomorph - _ClearVisitedFlags(theGraph); - - if (PERTINENT(theGraph, IC->w)) - { - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - } - else - { - IC->z = IC->w; - if (_FindUnembeddedEdgeToAncestor(theGraph, IC->z, &IC->uz, &IC->dz) != TRUE) - return NOTOK; - } - - // The X-Y path doesn't have to be the same one that was associated with the - // separating internal edge. - if (_SetVertexTypesForMarkingXYPath(theGraph) != OK || - _MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - // Isolate the K4 homeomorph - if (_K4_IsolateMinorB2(theGraph) != OK || - _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - // Indicate success by returning NONEMBEDDABLE - return NONEMBEDDABLE; - } - - // If K_4 homeomorph not found, make reductions along a_x and a_y paths. - if (a_x == a_y) - { - // In the special case where both paths lead to the same vertex, we can - // reduce the bicomp to a single edge, which avoids issues of reversed - // orientation between the bicomp root and the vertex. - if (_K4_ReduceBicompToEdge(theGraph, context, R, a_x) != OK) - return NOTOK; - } - else - { - // When a_x and a_y are distinct, we reduce each path from root to the vertex - if (_K4_ReducePathComponent(theGraph, context, R, 1, a_x) != OK || - _K4_ReducePathComponent(theGraph, context, R, 0, a_y) != OK) - return NOTOK; - } - - // Return OK to indicate that WalkDown processing may proceed to resolve - // more of the pertinence of this bicomp. - return OK; - } - - // Minor E indicates the desired K4 homeomorph, so we isolate it and return NONEMBEDDABLE - else if (theGraph->IC.minorType & MINORTYPE_E) - { - // Reality check on stack state - if (sp_NonEmpty(theGraph->theStack)) - return NOTOK; - - // Impose consistent orientation on the embedding so we can then - // restore the reduced paths. - if (_OrientVerticesInEmbedding(theGraph) != OK || - _K4_RestoreAndOrientReducedPaths(theGraph, context) != OK) - return NOTOK; - - // Set up to isolate minor E - _ClearVisitedFlags(theGraph); - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - - if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) - return NOTOK; - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - // Isolate the K4 homeomorph - if (_IsolateOuterplanarityObstructionE(theGraph) != OK || - _DeleteUnmarkedVerticesAndEdges(theGraph) != OK) - return NOTOK; - - // Return indication that K4 homeomorph has been found - return NONEMBEDDABLE; - } - - // You never get here in an error-free implementation like this one - return NOTOK; -} - -/**************************************************************************** - _K4_ChooseTypeOfNonOuterplanarityMinor() - This is an overload of the function _ChooseTypeOfNonOuterplanarityMinor() - that avoids processing the whole bicomp rooted by R, e.g. to orient its - vertices or label the vertices of its external face. - This is necessary in particular because of the reduction processing on - MINORTYPE_B. When a K2,3 is found by minor B, we may not be able to find - an entangled K4, so a reduction is performed, but it only eliminates - part of the bicomp and the operations here need to avoid touching parts - of the bicomp that won't be reduced, except by a constant amount of course. - ****************************************************************************/ - -int _K4_ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R) -{ - int XPrevLink=1, YPrevLink=0; - int Wx, WxPrevLink, Wy, WyPrevLink; - - _InitIsolatorContext(theGraph); - - theGraph->IC.v = v; - theGraph->IC.r = R; - - // Reality check on data structure integrity - if (!gp_VirtualVertexInUse(theGraph, R)) - return NOTOK; - - // We are essentially doing a _FindActiveVertices() here, except two things: - // 1) for outerplanarity we know the first vertices along the paths from R - // are the desired vertices because no vertices are "inactive" - // 2) We have purposely not oriented the bicomp, so the XPrevLink result is - // needed to help find the pertinent vertex W - theGraph->IC.x = _GetNeighborOnExtFace(theGraph, R, &XPrevLink); - theGraph->IC.y = _GetNeighborOnExtFace(theGraph, R, &YPrevLink); - - // We are essentially doing a _FindPertinentVertex() here, except two things: - // 1) It is not known whether the reduction of the path through X or the path - // through Y will enable the pertinence of W to be resolved, so it is - // necessary to perform parallel face traversal to find W with a cost no - // more than twice what it will take to resolve the W's pertinence - // (assuming we have to do a reduction rather than finding an entangled K4) - // 2) In the normal _FindPertinentVertex(), the bicomp is already oriented, so - // the "prev link" is hard coded to traverse down the X side. In this - // implementation, the bicomp is purposely not oriented, so we need to know - // XPrevLink and YPrevLink in order to set off in the correct directions. - Wx = theGraph->IC.x; - WxPrevLink = XPrevLink; - Wy = theGraph->IC.y; - WyPrevLink = YPrevLink; - theGraph->IC.w = NIL; - - while (Wx != theGraph->IC.y) - { - Wx = _GetNeighborOnExtFace(theGraph, Wx, &WxPrevLink); - if (PERTINENT(theGraph, Wx)) - { - theGraph->IC.w = Wx; - break; - } - Wy = _GetNeighborOnExtFace(theGraph, Wy, &WyPrevLink); - if (PERTINENT(theGraph, Wy)) - { - theGraph->IC.w = Wy; - break; - } - } - - if (gp_IsNotVertex(theGraph->IC.w)) - return NOTOK; - - // If the root copy is not a root copy of the current vertex v, - // then the Walkdown terminated on a descendant bicomp, which is Minor A. - if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) - theGraph->IC.minorType |= MINORTYPE_A; - - // If W has a pertinent child bicomp, then we've found Minor B. - // Notice this is different from planarity, in which minor B is indicated - // only if the pertinent child bicomp is also future pertinent. - else if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theGraph->IC.w))) - theGraph->IC.minorType |= MINORTYPE_B; - - // The only other result is minor E (we will search for the X-Y path later) - else - theGraph->IC.minorType |= MINORTYPE_E; - - return OK; -} - -/**************************************************************************** - _K4_FindSecondActiveVertexOnLowExtFacePath() - - This method is used in the processing of obstruction A, so it can take - advantage of the bicomp being oriented beforehand. - - This method determines whether there is an active vertex Z other than W on - the path [X, ..., W, ..., Y]. By active, we mean a vertex that connects - by an unembedded edge to either v or an ancestor of v. That is, a vertex - that is pertinent or future pertinent (would be pertinent in a future step - of the embedder). - ****************************************************************************/ - -int _K4_FindSecondActiveVertexOnLowExtFacePath(graphP theGraph) -{ - int Z=theGraph->IC.r, ZPrevLink=1; - - // First we test X for future pertinence only (if it were pertinent, then - // we wouldn't have been blocked up on this bicomp) - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) - { - theGraph->IC.z = Z; - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); - return TRUE; - } - - // Now we move on to test all the vertices strictly between X and Y on - // the lower external face path, except W, for either pertinence or - // future pertinence. - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - - while (Z != theGraph->IC.y) - { - if (Z != theGraph->IC.w) - { - gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) - { - theGraph->IC.z = Z; - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); - return TRUE; - } - else if (PERTINENT(theGraph, Z)) - { - theGraph->IC.z = Z; - theGraph->IC.uz = theGraph->IC.v; - return TRUE; - } - } - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - // Now we test Y for future pertinence (same explanation as for X above) - gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraph->IC.v); - if (FUTUREPERTINENT(theGraph, Z, theGraph->IC.v)) - { - theGraph->IC.z = Z; - theGraph->IC.uz = _GetLeastAncestorConnection(theGraph, Z); - return TRUE; - } - - // We didn't find the desired second vertex, so report FALSE - return FALSE; -} - -/**************************************************************************** - _K4_FindPlanarityActiveVertex() - This service routine starts out at R and heads off in the direction opposite - the prevLink to find the first "planarity active" vertex, i.e. the first one - that is pertinent or future pertinent. - ****************************************************************************/ - -int _K4_FindPlanarityActiveVertex(graphP theGraph, int v, int R, int prevLink, int *pW) -{ - int W = R, WPrevLink = prevLink; - - W = _GetNeighborOnExtFace(theGraph, R, &WPrevLink); - - while (W != R) - { - if (PERTINENT(theGraph, W)) - { - *pW = W; - return OK; - } - else - { - gp_UpdateVertexFuturePertinentChild(theGraph, W, v); - if (FUTUREPERTINENT(theGraph, W, v)) - { - *pW = W; - return OK; - } - } - - W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); - } - - return NOTOK; -} - -/**************************************************************************** - _K4_FindSeparatingInternalEdge() - - Logically, this method is similar to calling MarkHighestXYPath() to - see if there is an internal separator between R and A. - However, that method cannot be called because the bicomp is not oriented. - - Because this is an outerplanarity related algorithm, there are no internal - vertices to contend with, so it is easier to inspect the internal edges - incident to each vertex internal to the path (R ... A), i.e. excluding endpoints, - to see whether any of the edges connects outside of the path [R ... A], - including endpoints. - - In order to avoid adding cost to the core planarity algorithm, we'll just use - the fact that the vertex obstruction types are pre-initialized to the unmarked - state. Until we know if there is a separating internal edge, we cannot be sure - that we can isolate a K4 homeomorph, so we can't afford to initialize the whole - bicomp. The obstruction type of each vertex along the path [R ... A] is marked. - Then, for each vertex in the range (R ... A), if there is any edge that is also - incident to a vertex whose obstruction type is still unmarked, then that edge - is the desired separator edge between R and W (i.e. A), and so we save information - about it. - - If the separator edge is found, then this method sets the *pW to A, and it - sets *pX and *pY values with the endpoints of the separator edge. - The visited flags of the separator edge and its endpoint vertices are not set - at this time because it is easier to set them later as part of the overall K4 - homeomorph isolation. - - Lastly, we restore the unmarked obstruction type settings on the path [R ... A]. - - Returns TRUE if separator edge found or FALSE otherwise - ****************************************************************************/ - -int _K4_FindSeparatingInternalEdge(graphP theGraph, int R, int prevLink, int A, int *pW, int *pX, int *pY) -{ - int Z, ZPrevLink, e, neighbor; - - // Mark the vertex obstruction type settings along the path [R ... A] - _K4_MarkObstructionTypeOnExternalFacePath(theGraph, R, prevLink, A); - - // Search each of the vertices in the range (R ... A) - *pX = *pY = NIL; - ZPrevLink = prevLink; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - while (Z != A) - { - // Search for a separator among the edges of Z - // It is OK to not bother skipping the external face edges, since we - // know they are marked visited and so are ignored - e = gp_GetFirstArc(theGraph, Z); - while (gp_IsArc(e)) - { - neighbor = gp_GetNeighbor(theGraph, e); - if (gp_GetVertexObstructionType(theGraph, neighbor) == VERTEX_OBSTRUCTIONTYPE_UNMARKED) - { - *pW = A; - *pX = Z; - *pY = neighbor; - break; - } - e = gp_GetNextArc(theGraph, e); - } - - // If we found the separator edge, then we don't need to go on - if (gp_IsVertex(*pX)) { - break; - } - - // Go to the next vertex - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - // Restore the unmarked obstruction type settings on the path [R ... A] - _K4_UnmarkObstructionTypeOnExternalFacePath(theGraph, R, prevLink, A); - - return gp_IsVertex(*pX) ? TRUE : FALSE; -} - -/**************************************************************************** - _K4_MarkObstructionTypeOnExternalFacePath() - - Assumes A is a vertex along the external face of the bicomp rooted by R. - Marks the obstruction type of vertices along the path (R ... A) that begins - with R's link[1^prevLink] arc. - ****************************************************************************/ - -void _K4_MarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A) -{ - int Z, ZPrevLink; - - gp_SetVertexObstructionType(theGraph, R, VERTEX_OBSTRUCTIONTYPE_MARKED); - ZPrevLink = prevLink; - Z = R; - while (Z != A) - { - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - gp_SetVertexObstructionType(theGraph, Z, VERTEX_OBSTRUCTIONTYPE_MARKED); - } -} - -/**************************************************************************** - _K4_UnmarkObstructionTypeOnExternalFacePath() - - Assumes A is a vertex along the external face of the bicomp rooted by R. - Unmarks the obstruction type of vertices along the path (R ... A) that begins - with R's link[1^prevLink] arc. - ****************************************************************************/ - -void _K4_UnmarkObstructionTypeOnExternalFacePath(graphP theGraph, int R, int prevLink, int A) -{ - int Z, ZPrevLink; - - gp_ClearVertexObstructionType(theGraph, R); - ZPrevLink = prevLink; - Z = R; - while (Z != A) - { - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - gp_ClearVertexObstructionType(theGraph, Z); - } -} - -/**************************************************************************** - _K4_IsolateMinorA1() - - This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get - a K_4 via the additional path from some vertex Z to the current vertex. - This path may be via some descendant of Z, and it may be a future pertinent - connection to an ancestor of the current vertex. - ****************************************************************************/ - -int _K4_IsolateMinorA1(graphP theGraph) -{ - isolatorContextP IC = &theGraph->IC; - - if (IC->uz < IC->v) - { - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->uz, IC->v) != OK) - return NOTOK; - } - - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->z, IC->dz) != OK) - return NOTOK; - - if (_IsolateOuterplanarityObstructionA(theGraph) != OK) - return NOTOK; - - if (_AddAndMarkEdge(theGraph, IC->uz, IC->dz) != OK) { - return NOTOK; - } - - return OK; -} - -/**************************************************************************** - _K4_IsolateMinorA2() - - This pattern is essentially outerplanarity minor A, a K_{2,3}, except we get - a K_4 via an additional X-Y path within the main bicomp, which is guaranteed - to exist by the time this method is invoked. - One might think to simply invoke _MarkHighestXYPath() to obtain the path, - but the IC->px and IC->py values are already set before invoking this method, - and the bicomp is outerplanar, so the XY path is just an edge. Also, one - subcase of pattern B2 reduces to this pattern, except that the XY path is - determined by the B2 isolator. - ****************************************************************************/ - -int _K4_IsolateMinorA2(graphP theGraph) -{ - isolatorContextP IC = &theGraph->IC; - - // We assume the X-Y path was already marked - if (!gp_GetVertexVisited(theGraph, IC->px) || !gp_GetVertexVisited(theGraph, IC->py)) - return NOTOK; - - return _IsolateOuterplanarityObstructionA(theGraph); -} - -/**************************************************************************** - _K4_IsolateMinorB1() - - It is possible to get a K_4 based on the pertinence of w, but we don't do it - that way. If we use the pertinence of w, then we have to eliminate part of - the bicomp external face, which has special cases if a_x==w or a_y==w. - Typically we would mark (r ... a_x ... w ... a_y), which works even when a_y==w, - but if instead a_x==w, then we'd have to mark (w ... a_y ... r). - - Since a_x and a_y are guaranteed to be distinct, it is easier to just ignore - the pertinence of w, and instead use the lower bicomp external face path - as the connection between a_x and a_y. This includes w, but then the - isolation process is the same even if a_x==w or a_y==w. The other two - connections for a_x and a_y are to v and MAX(ux, uy). - ****************************************************************************/ - -int _K4_IsolateMinorB1(graphP theGraph) -{ - isolatorContextP IC = &theGraph->IC; - - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->x, IC->dx) != OK) - return NOTOK; - - if (theGraph->functions.fpMarkDFSPath(theGraph, IC->y, IC->dy) != OK) - return NOTOK; - - // The path from the bicomp root to MIN(ux,uy) is marked to ensure the - // connection from the image vertices v and MAX(ux,uy) as well as the - // connection from MAX(ux,uy) through MIN(ux,uy) to (ux==MIN(ux,uy)?x:y) - if (theGraph->functions.fpMarkDFSPath(theGraph, MIN(IC->ux, IC->uy), IC->r) != OK) - return NOTOK; - - // This makes the following connections (a_x ... v), (a_y ... v), and - // (a_x ... w ... a_y), the last being tolerant of a_x==w or a_y==w - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK) - return NOTOK; - - if (_JoinBicomps(theGraph) != OK) - return NOTOK; - - if (_AddAndMarkEdge(theGraph, IC->ux, IC->dx) != OK) - return NOTOK; - - if (_AddAndMarkEdge(theGraph, IC->uy, IC->dy) != OK) { - return NOTOK; - } - - return OK; -} - -/**************************************************************************** - _K4_IsolateMinorB2() - - The first subcase of B2 can be reduced to outerplanarity obstruction E - The second subcase of B2 can be reduced to A2 by changing v to u - ****************************************************************************/ - -int _K4_IsolateMinorB2(graphP theGraph) -{ - isolatorContextP IC = &theGraph->IC; - - // First subcase, the active vertex is pertinent - if (PERTINENT(theGraph, IC->w)) - { - // We assume the X-Y path was already marked - if (!gp_GetVertexVisited(theGraph, IC->px) || !gp_GetVertexVisited(theGraph, IC->py)) - return NOTOK; - - return _IsolateOuterplanarityObstructionE(theGraph); - } - - // Second subcase, the active vertex is future pertinent - else if (FUTUREPERTINENT(theGraph, IC->w, IC->v)) - { - IC->v = IC->uz; - IC->dw = IC->dz; - - return _K4_IsolateMinorA2(theGraph); - } - - return OK; -} - -/**************************************************************************** - _K4_ReduceBicompToEdge() - - This method is used when reducing the main bicomp of obstruction A to a - single edge (R, W). We first delete all edges from the bicomp except - those on the DFS tree path W to R, then we reduce that DFS tree path to - a DFS tree edge. - - After the reduction, the outerplanarity Walkdown traversal can continue - R to W without being blocked as was the case when R was adjacent to X and Y. - - Returns OK for success, NOTOK for internal (implementation) error. - ****************************************************************************/ - -int _K4_ReduceBicompToEdge(graphP theGraph, K4SearchContext *context, int R, int W) -{ - int newEdge; - - if (_OrientVerticesInBicomp(theGraph, R, 0) != OK || - _ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - if (theGraph->functions.fpMarkDFSPath(theGraph, R, W) != OK) - return NOTOK; - if (_K4_DeleteUnmarkedEdgesInBicomp(theGraph, context, R) != OK) - return NOTOK; - - // Now we have to reduce the path W -> R to the DFS tree edge (R, W) - newEdge =_K4_ReducePathToEdge(theGraph, context, EDGE_TYPE_PARENT, - R, gp_GetFirstArc(theGraph, R), W, gp_GetFirstArc(theGraph, W)); - if (gp_IsNotArc(newEdge)) - return NOTOK; - - // Finally, set the visited info state of W to unvisited so that - // the core embedder (esp. Walkup) will not have any problems. - gp_SetVertexVisitedInfo(theGraph, W, theGraph->N); - - return OK; -} - -/**************************************************************************** - _K4_ReducePathComponent() - - This method is invoked when the bicomp rooted by R contains a component - subgraph that is separable from the bicomp by the 2-cut (R, A). The K_4 - homeomorph isolator will have processed a significant fraction of the - component, and so it must be reduced to an edge to ensure that said - processing happens at most once on the component (except for future - operations that are bound to linear time in total by other arguments). - - Because the bicomp is an outerplanar embedding, the component is known to - consists of an external face path plus some internal edges that are parallel - to that path. Otherwise, it wouldn't be separable by the 2-cut (R, A). - - The goal of this method is to reduce the component to the edge (R, A). This - is done in such a way that, if the reduction must be restored, the DFS tree - structure connecting the restored vertices is retained. - - The first step is to ensure that (R, A) is not already just an edge, in which - case no reduction is needed. This can occur if A is future pertinent. - - Assuming a non-trivial reduction component, the next step is to determine - the DFS tree structure within the component. Because it is separable by the - 2-cut (R, A), there are only two cases: - - Case 1: The DFS tree path from A to R is within the reduction component. - - In this case, the DFS tree path is marked, the remaining edges of the - reduction component are eliminated, and then the DFS tree path is reduced to - the the tree edge (R, A). - - Note that the reduction component may also contain descendants of A as well - as vertices that are descendant to R but are neither ancestors nor - descendants of A. This depends on where the tree edge from R meets the - external face path (R ... A). However, the reduction component can only - contribute one path to any future K_4, so it suffices to preserve only the - DFS tree path (A --> R). - - Case 2: The DFS tree path from A to R is not within the reduction component. - - In this case, the external face edge from R leads to a descendant D of A. - We mark that back edge (R, D) plus the DFS tree path (D --> A). The - remaining edges of the reduction component can be removed, and then the - path (R, D, ..., A) is reduced to the edge (R, A). - - For the sake of contradiction, suppose that only part of the DFS tree path - from A to R were contained by the reduction component. Then, a DFS tree edge - would have to exit the reduction component and connect to some vertex not - on the external face path (R, ..., A). This contradicts the assumption that - the reduction subgraph is separable from the bicomp by the 2-cut (R, A). - - Returns OK for success, NOTOK for internal (implementation) error. - ****************************************************************************/ - -int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, int prevLink, int A) -{ - int e_R, e_A, Z, ZPrevLink, edgeType, invertedFlag=0; - - // Check whether the external face path (R, ..., A) is just an edge - e_R = gp_GetArc(theGraph, R, 1^prevLink); - if (gp_GetNeighbor(theGraph, e_R) == A) - return OK; - - // Check for Case 1: The DFS tree path from A to R is within the reduction component - if (_K4_TestPathComponentForAncestor(theGraph, R, prevLink, A)) - { - _K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A); - if (theGraph->functions.fpMarkDFSPath(theGraph, R, A) != OK) - return NOTOK; - edgeType = EDGE_TYPE_PARENT; - - invertedFlag = _K4_GetCumulativeOrientationOnDFSPath(theGraph, R, A); - } - - // Otherwise Case 2: The DFS tree path from A to R is not within the reduction component - else - { - _K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A); - Z = gp_GetNeighbor(theGraph, e_R); - gp_SetEdgeVisited(theGraph, e_R); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e_R)); - if (theGraph->functions.fpMarkDFSPath(theGraph, A, Z) != OK) { - return NOTOK; - } - edgeType = EDGE_TYPE_BACK; - } - - // The path to be kept/reduced is marked, so the other edges can go - if (_K4_DeleteUnmarkedEdgesInPathComponent(theGraph, R, prevLink, A) != OK) - return NOTOK; - - // Clear all the visited flags for safety, except the vertices R and A - // will remain in the embedding, and the core embedder (Walkup) uses a - // value greater than the current vertex to indicate an unvisited vertex - _K4_ClearVisitedInPathComponent(theGraph, R, prevLink, A); - gp_SetVertexVisitedInfo(theGraph, A, theGraph->N); - - // Find the component's remaining edges e_A and e_R incident to A and R - ZPrevLink = prevLink; - Z = R; - while (Z != A) - { - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - e_A = gp_GetArc(theGraph, A, ZPrevLink); - e_R = gp_GetArc(theGraph, R, 1^prevLink); - - // Reduce the path (R ... A) to an edge - e_R = _K4_ReducePathToEdge(theGraph, context, edgeType, R, e_R, A, e_A); - if (gp_IsNotArc(e_R)) - return NOTOK; - - // Preserve the net orientation along the DFS path in the case of a tree edge - if (gp_GetEdgeType(theGraph, e_R) == EDGE_TYPE_CHILD) - { - if (invertedFlag) - gp_SetEdgeFlagInverted(theGraph, e_R); - } - - return OK; -} - -/******************************************************************** - Edge deletion that occurs during a reduction or restoration of a - reduction is augmented by clearing the K_4 search-specific - data members. This is augmentation is not needed in the delete edge - operations that happen once a K_4 homeomorph has been found and - marked for isolation. - ********************************************************************/ - -int _K4_DeleteEdge(graphP theGraph, K4SearchContext *context, int e, int nextLink) -{ - _K4Search_InitEdgeRec(context, e); - _K4Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e)); - - return gp_DeleteEdge(theGraph, e, nextLink); -} - -/******************************************************************** - _K4_DeleteUnmarkedEdgesInBicomp() - - This function deletes from a given biconnected component all edges - whose visited member is zero. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - This is the same as _DeleteUnmarkedEdgesInBicomp(), except it calls - the overloaded _K4_DeleteEdge() rather than gp_DeleteEdge() - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _K4_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K4SearchContext *context, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetEdgeVisited(theGraph, e) - ? gp_GetNextArc(theGraph, e) - : _K4_DeleteEdge(theGraph, context, e, 0); - } - } - return OK; -} - -/**************************************************************************** - _K4_GetCumulativeOrientationOnDFSPath() - ****************************************************************************/ -int _K4_GetCumulativeOrientationOnDFSPath(graphP theGraph, int ancestor, int descendant) -{ -int e, parent; -int invertedFlag=0; - - /* If we are marking from a root vertex upward, then go up to the parent - copy before starting the loop */ - - if (gp_IsVirtualVertex(theGraph, descendant)) - descendant = gp_GetPrimaryVertexFromRoot(theGraph, descendant); - - while (descendant != ancestor) - { - if (gp_IsNotVertex(descendant)) - return NOTOK; - - // If we are at a bicomp root, then ascend to its parent copy - if (gp_IsVirtualVertex(theGraph, descendant)) - { - parent = gp_GetPrimaryVertexFromRoot(theGraph, descendant); - } - - // If we are on a regular, non-virtual vertex then get the edge to the parent - else - { - // Scan the edges for the one marked as the DFS parent - parent = NIL; - e = gp_GetFirstArc(theGraph, descendant); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT) - { - parent = gp_GetNeighbor(theGraph, e); - break; - } - e = gp_GetNextArc(theGraph, e); - } - - // If the edge to the parent vertex was not found, then the data structure is corrupt - if (gp_IsNotVertex(parent)) - return NOTOK; - - // Add the inversion flag on the child arc to the cumulative result - e = gp_GetTwinArc(theGraph, e); - if (gp_GetEdgeType(theGraph, e) != EDGE_TYPE_CHILD || gp_GetNeighbor(theGraph, e) != descendant) - return NOTOK; - invertedFlag ^= gp_GetEdgeFlagInverted(theGraph, e); - } - - // Hop to the parent and reiterate - descendant = parent; - } - - return invertedFlag; -} - -/**************************************************************************** - _K4_TestPathComponentForAncestor() - Tests the external face path between R and A for a DFS ancestor of A. - Returns TRUE if found, FALSE otherwise. - ****************************************************************************/ - -int _K4_TestPathComponentForAncestor(graphP theGraph, int R, int prevLink, int A) -{ - int Z, ZPrevLink; - - ZPrevLink = prevLink; - Z = R; - while (Z != A) - { - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - if (Z < A) - return TRUE; - } - return FALSE; -} - -/**************************************************************************** - _K4_ClearVisitedInPathComponent() - - There is a subcomponent of the bicomp rooted by R that is separable by the - 2-cut (R, A). The component contains the external face path from R to A. - The 1^prevLink arc of R is contained in that path (i.e. the first arc if - prevLink indicates the last, or the last arc if prevLink indicates the first). - The prevLink is passed because _GetNeighborOnExtFace() uses the - opposing link to traverse to the "next" vertex. - - All vertices in this desired component are along the external face, so we - traverse along the external face vertices strictly between R and A and - clear all the visited flags of the edges and their incident vertices. - - Note that the vertices along the path (R ... A) only have edges incident - to each other and to R and A because the component is separable by the - (R, A)-cut. - ****************************************************************************/ - -void _K4_ClearVisitedInPathComponent(graphP theGraph, int R, int prevLink, int A) -{ - int Z, ZPrevLink, e; - - ZPrevLink = prevLink; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - while (Z != A) - { - gp_ClearVertexVisited(theGraph, Z); - e = gp_GetFirstArc(theGraph, Z); - while (gp_IsArc(e)) - { - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } -} - -/**************************************************************************** - _K4_DeleteUnmarkedEdgesInPathComponent() - - There is a subcomponent of the bicomp rooted by R that is separable by the - 2-cut (R, A) and contains the external face path from R to A that includes - the arc gp_GetArc(theGraph, R, 1^prevLink), which is the first arc traversed - by _GetNeighborOnExtFace(..., &prevLink). - - The edges in the component have been marked unvisited except for a path we - intend to preserve. This routine deletes the unvisited edges. - - NOTE: This reduction invalidates the short-circuit extFace data structure, - but it will be repaired for use by WalkUp and WalkDown when the path - component reduction is completed. - - Returns OK on success, NOTOK on internal error - ****************************************************************************/ - -int _K4_DeleteUnmarkedEdgesInPathComponent(graphP theGraph, int R, int prevLink, int A) -{ - int Z, ZPrevLink, e; - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context == NULL) { - return NOTOK; - } - - // We need to use the stack to store up the edges we're going to delete. - // We want to make sure there is enough stack capacity to handle it, - // which is of course true because the stack is supposed to be empty. - // We're doing a reduction on a bicomp on which the WalkDown has completed, - // so the stack contains no bicomp roots to merge. - if (sp_NonEmpty(theGraph->theStack)) - return NOTOK; - - // Traverse all vertices internal to the path (R ... A) and push - // all non-visited edges - ZPrevLink = prevLink; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - while (Z != A) - { - e = gp_GetFirstArc(theGraph, Z); - while (gp_IsArc(e)) - { - // The comparison of e to its twin is a useful way of ensuring we - // don't push the edge twice, which is of course only applicable - // when processing an edge whose endpoints are both internal to - // the path (R ... A) - if (!gp_GetEdgeVisited(theGraph, e) && - (e < gp_GetTwinArc(theGraph, e) || - gp_GetNeighbor(theGraph, e) == R || gp_GetNeighbor(theGraph, e) == A)) - { - sp_Push(theGraph->theStack, e); - } - - e = gp_GetNextArc(theGraph, e); - } - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - // Delete all the non-visited edges - while (sp_NonEmpty(theGraph->theStack)) - { - sp_Pop(theGraph->theStack, e); - _K4_DeleteEdge(theGraph, context, e, 0); - } - - return OK; -} - -/**************************************************************************** - _K4_ReducePathToEdge() - - Returns an arc of the edge created on success, a non-arc (NOTOK) on failure - On success, the arc is in the adjacency list of R. The result can be tested - for success or failure using comparison with NIL (non-NIL being success) - ****************************************************************************/ - -int _K4_ReducePathToEdge(graphP theGraph, K4SearchContext *context, int edgeType, int R, int e_R, int A, int e_A) -{ - // Find out the links used in vertex R for edge e_R and in vertex A for edge e_A - int Rlink = gp_GetFirstArc(theGraph, R) == e_R ? 0 : 1; - int Alink = gp_GetFirstArc(theGraph, A) == e_A ? 0 : 1; - - // If the path is more than a single edge, then it must be reduced to an edge. - // Note that even if the path is a single edge, the external face data structure - // must still be modified since many edges connecting the external face have - // been deleted - if (gp_GetNeighbor(theGraph, e_R) != A) - { - int v_R, v_A; - - // Prepare for removing each of the two edges that join the path to the bicomp by - // restoring it if it is a reduction edge (a constant time operation) - if (gp_IsVertex(context->E[e_R].pathConnector)) - { - if (_K4_RestoreReducedPath(theGraph, context, e_R) != OK) - return NOTOK; - - e_R = gp_GetArc(theGraph, R, Rlink); - } - - if (gp_IsVertex(context->E[e_A].pathConnector)) - { - if (_K4_RestoreReducedPath(theGraph, context, e_A) != OK) - return NOTOK; - e_A = gp_GetArc(theGraph, A, Alink); - } - - // Save the vertex neighbors of R and A indicated by e_R and e_A for - // later use in setting up the path connectors. - v_R = gp_GetNeighbor(theGraph, e_R); - v_A = gp_GetNeighbor(theGraph, e_A); - - // Now delete the two edges that join the path to the bicomp. - _K4_DeleteEdge(theGraph, context, e_R, 0); - _K4_DeleteEdge(theGraph, context, e_A, 0); - - // Now add a single edge to represent the path - // We use 1^Rlink, for example, because Rlink was the link from R that indicated e_R, - // so 1^Rlink is the link that indicated e_R in the other arc that was adjacent to e_R. - // We want gp_InsertEdge to place the new arc where e_R was in R's adjacency list - gp_InsertEdge(theGraph, R, gp_GetArc(theGraph, R, Rlink), 1^Rlink, - A, gp_GetArc(theGraph, A, Alink), 1^Alink); - - // Now set up the path connectors so the original path can be recovered if needed. - e_R = gp_GetArc(theGraph, R, Rlink); - context->E[e_R].pathConnector = v_R; - - e_A = gp_GetArc(theGraph, A, Alink); - context->E[e_A].pathConnector = v_A; - - // Also, set the reduction edge's type to preserve the DFS tree structure - gp_SetEdgeType(theGraph, e_R, _ComputeArcType(theGraph, R, A, edgeType)); - gp_SetEdgeType(theGraph, e_A, _ComputeArcType(theGraph, A, R, edgeType)); - } - - // Set the external face data structure - gp_SetExtFaceVertex(theGraph, R, Rlink, A); - gp_SetExtFaceVertex(theGraph, A, Alink, R); - - // If the edge represents an entire bicomp, then more external face - // settings are needed. - if (gp_GetFirstArc(theGraph, R) == gp_GetLastArc(theGraph, R)) - { - gp_SetExtFaceVertex(theGraph, R, 1^Rlink, A); - gp_SetExtFaceVertex(theGraph, A, 1^Alink, R); - } - - return e_R; -} - -/**************************************************************************** - _K4_RestoreReducedPath() - - Given an edge record of an edge used to reduce a path, we want to restore - the path in constant time. - The path may contain more reduction edges internally, but we do not - search for and process those since it would violate the constant time - bound required of this function. - - Note that we don't bother amending the external face data structure because - a reduced path is only restored when it will shortly be reduced again or - when we don't really need the external face data structure anymore. - - Return OK on success, NOTOK on failure - ****************************************************************************/ - -int _K4_RestoreReducedPath(graphP theGraph, K4SearchContext *context, int e) -{ -int eTwin, u, v, w, x; -int e0, e1, eTwin0, eTwin1; - - if (gp_IsNotVertex(context->E[e].pathConnector)) - return OK; - - eTwin = gp_GetTwinArc(theGraph, e); - - u = gp_GetNeighbor(theGraph, eTwin); - v = context->E[e].pathConnector; - w = context->E[eTwin].pathConnector; - x = gp_GetNeighbor(theGraph, e); - - // Get the locations of the EdgeRecs between which the new EdgeRecs - // must be added in order to reconnect the path parallel to the edge. - e0 = gp_GetNextArc(theGraph, e); - e1 = gp_GetPrevArc(theGraph, e); - eTwin0 = gp_GetNextArc(theGraph, eTwin); - eTwin1 = gp_GetPrevArc(theGraph, eTwin); - - // We first delete the edge represented by e and eTwin. We do so before - // restoring the path to ensure we do not exceed the maximum arc capacity. - _K4_DeleteEdge(theGraph, context, e, 0); - - // Now we add the two edges to reconnect the reduced path represented - // by the edge [e, eTwin]. The edge record in u is added between e0 and e1. - // Likewise, the new edge record in x is added between eTwin0 and eTwin1. - if (gp_IsArc(e0)) - { - if (gp_InsertEdge(theGraph, u, e0, 1, v, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, u, e1, 0, v, NIL, 0) != OK) - return NOTOK; - } - - if (gp_IsArc(eTwin0)) - { - if (gp_InsertEdge(theGraph, x, eTwin0, 1, w, NIL, 0) != OK) - return NOTOK; - } - else - { - if (gp_InsertEdge(theGraph, x, eTwin1, 0, w, NIL, 0) != OK) - return NOTOK; - } - - // Set the types of the newly added edges. In both cases, the first of the two - // vertex parameters is known to be degree 2 because they are internal to the - // path being restored, so this operation is constant time. - if (_SetEdgeType(theGraph, v, u) != OK || _SetEdgeType(theGraph, w, x) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _K4_RestoreAndOrientReducedPaths() - - This function searches the embedding for any edges that are specially marked - as being representative of a path that was previously reduced to a single edge - by _ReducePathToEdge(). This method restores the path by replacing the edge - with the path. - - Note that the new path may contain more reduction edges, and these will be - iteratively expanded by the outer for loop. Equally, a reduced path may - be restored into a path that itself is a reduction path that will only be - attached to the embedding by some future step of the outer loop. - - The vertices along the path being restored must be given a consistent - orientation with the endpoints. It is expected that the embedding - will have been oriented prior to this operation. - ****************************************************************************/ - -int _K4_RestoreAndOrientReducedPaths(graphP theGraph, K4SearchContext *context) -{ - int EsizeOccupied, e, eTwin, u, v, w, x, visited; - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied;) - { - if (gp_IsVertex(context->E[e].pathConnector)) - { - visited = gp_GetEdgeVisited(theGraph, e); - - eTwin = gp_GetTwinArc(theGraph, e); - u = gp_GetNeighbor(theGraph, eTwin); - v = context->E[e].pathConnector; - w = context->E[eTwin].pathConnector; - x = gp_GetNeighbor(theGraph, e); - - if (_K4_RestoreReducedPath(theGraph, context, e) != OK) - return NOTOK; - - // If the path is on the external face, orient it - if (gp_GetNeighbor(theGraph, gp_GetFirstArc(theGraph, u)) == v || - gp_GetNeighbor(theGraph, gp_GetLastArc(theGraph, u)) == v) - { - // Reality check: ensure the path is connected to the - // external face at both vertices. - if (gp_GetNeighbor(theGraph, gp_GetFirstArc(theGraph, x)) != w && - gp_GetNeighbor(theGraph, gp_GetLastArc(theGraph, x)) != w) - return NOTOK; - - if (_OrientExternalFacePath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - - if (visited) - { - if (_SetVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - else - { - if (_ClearVisitedFlagsOnPath(theGraph, u, v, w, x) != OK) - return NOTOK; - } - } - else e+=2; - } - - return OK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.h deleted file mode 100644 index d9af8c6c5..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef GRAPH_K4SEARCH_H -#define GRAPH_K4SEARCH_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graphStructures.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define K4SEARCH_NAME "K4Search" - -int gp_AttachK4Search(graphP theGraph); -int gp_DetachK4Search(graphP theGraph); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.private.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.private.h deleted file mode 100644 index 66240820a..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search.private.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef GRAPH_K4SEARCH_PRIVATE_H -#define GRAPH_K4SEARCH_PRIVATE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graph.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Additional equipment for each EdgeRec - - pathConnector: - Used in the edge records (arcs) of a reduction edge to indicate the - endpoints of a path that has been reduced from (removed from) the - embedding so that the search for a K4 can continue. - We only need a pathConnector because we reduce subgraphs that are - separable by a 2-cut, so they can contribute at most one path to a - subgraph homeomorphic to K4, if one is indeed found. Thus, we first - delete all edges except for the desired path(s), then we reduce any - retained path to an edge. - */ -typedef struct -{ - int pathConnector; -} K4Search_EdgeRec; - -typedef K4Search_EdgeRec * K4Search_EdgeRecP; - -/* Additional equipment for each vertex: None */ - -typedef struct -{ - // Helps distinguish initialize from re-initialize - int initialized; - - // The graph that this context augments - graphP theGraph; - - // Parallel array for additional edge level equipment - K4Search_EdgeRecP E; - - // Overloaded function pointers - graphFunctionTable functions; - - // Internal variable for converting a tail recursion into a simple loop - int handlingBlockedBicomp; - -} K4SearchContext; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search_Extensions.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search_Extensions.c deleted file mode 100644 index 9338beb63..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphK4Search_Extensions.c +++ /dev/null @@ -1,511 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif -#include - -#include "graphK4Search.private.h" -#include "graphK4Search.h" - -extern int _SearchForK4InBicomp(graphP theGraph, K4SearchContext *context, int v, int R); - -extern int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, - int *degrees, int *imageVerts); - -extern int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, - int *imageVerts, int maxNumImageVerts); - -extern int _TestSubgraph(graphP theSubgraph, graphP theGraph); - -/* Forward declarations of local functions */ - -void _K4Search_ClearStructures(K4SearchContext *context); -int _K4Search_CreateStructures(K4SearchContext *context); -int _K4Search_InitStructures(K4SearchContext *context); - -void _K4Search_InitEdgeRec(K4SearchContext *context, int e); - -/* Forward declarations of overloading functions */ -int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph); - -int _K4Search_InitGraph(graphP theGraph, int N); -void _K4Search_ReinitializeGraph(graphP theGraph); -int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -/* Forward declarations of functions used by the extension system */ - -void *_K4Search_DupContext(void *pContext, void *theGraph); -void _K4Search_FreeContext(void *); - -/**************************************************************************** - * K4SEARCH_ID - the variable used to hold the integer identifier for this - * extension, enabling this feature's extension context to be distinguished - * from other features' extension contexts that may be attached to a graph. - ****************************************************************************/ - -int K4SEARCH_ID = 0; - -/**************************************************************************** - gp_AttachK4Search() - - This function adjusts the graph data structure to attach the K4 search - feature. - ****************************************************************************/ - -int gp_AttachK4Search(graphP theGraph) -{ - K4SearchContext *context = NULL; - - // If the K4 search feature has already been attached to the graph, - // then there is no need to attach it again - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - if (context != NULL) - { - return OK; - } - - // Allocate a new extension context - context = (K4SearchContext *) malloc(sizeof(K4SearchContext)); - if (context == NULL) - { - return NOTOK; - } - - // First, tell the context that it is not initialized - context->initialized = 0; - - // Save a pointer to theGraph in the context - context->theGraph = theGraph; - - // Put the overload functions into the context function table. - // gp_AddExtension will overload the graph's functions with these, and - // return the base function pointers in the context function table - memset(&context->functions, 0, sizeof(graphFunctionTable)); - context->functions.fpHandleBlockedBicomp = _K4Search_HandleBlockedBicomp; - context->functions.fpEmbedPostprocess = _K4Search_EmbedPostprocess; - context->functions.fpCheckEmbeddingIntegrity = _K4Search_CheckEmbeddingIntegrity; - context->functions.fpCheckObstructionIntegrity = _K4Search_CheckObstructionIntegrity; - - context->functions.fpInitGraph = _K4Search_InitGraph; - context->functions.fpReinitializeGraph = _K4Search_ReinitializeGraph; - context->functions.fpEnsureArcCapacity = _K4Search_EnsureArcCapacity; - - _K4Search_ClearStructures(context); - - // Store the K4 search context, including the data structure and the - // function pointers, as an extension of the graph - if (gp_AddExtension(theGraph, &K4SEARCH_ID, (void *) context, - _K4Search_DupContext, _K4Search_FreeContext, - &context->functions) != OK) - { - _K4Search_FreeContext(context); - return NOTOK; - } - - // Create the K4-specific structures if the size of the graph is known - // Attach functions are always invoked after gp_New(), but if a graph - // extension must be attached before gp_Read(), then the attachment - // also happens before gp_InitGraph(), which means N==0. - // However, sometimes a feature is attached after gp_InitGraph(), in - // which case N > 0 - if (theGraph->N > 0) - { - if (_K4Search_CreateStructures(context) != OK || - _K4Search_InitStructures(context) != OK) - { - _K4Search_FreeContext(context); - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - gp_DetachK4Search() - ********************************************************************/ - -int gp_DetachK4Search(graphP theGraph) -{ - return gp_RemoveExtension(theGraph, K4SEARCH_ID); -} - -/******************************************************************** - _K4Search_ClearStructures() - ********************************************************************/ - -void _K4Search_ClearStructures(K4SearchContext *context) -{ - if (!context->initialized) - { - // Before initialization, the pointers are stray, not NULL - // Once NULL or allocated, free() or LCFree() can do the job - context->E = NULL; - - context->handlingBlockedBicomp = FALSE; - - context->initialized = 1; - } - else - { - if (context->E != NULL) - { - free(context->E); - context->E = NULL; - } - context->handlingBlockedBicomp = FALSE; - } -} - -/******************************************************************** - _K4Search_CreateStructures() - Create uninitialized structures for the vertex and edge levels, and - initialized structures for the graph level - ********************************************************************/ -int _K4Search_CreateStructures(K4SearchContext *context) -{ - int Esize = gp_EdgeIndexBound(context->theGraph); - - if (context->theGraph->N <= 0) - return NOTOK; - - if ((context->E = (K4Search_EdgeRecP) malloc(Esize*sizeof(K4Search_EdgeRec))) == NULL || - 0) - { - return NOTOK; - } - - return OK; -} - -/******************************************************************** - _K4Search_InitStructures() - ********************************************************************/ -int _K4Search_InitStructures(K4SearchContext *context) -{ -#if NIL == 0 || NIL == -1 - memset(context->E, NIL_CHAR, gp_EdgeIndexBound(context->theGraph) * sizeof(K4Search_EdgeRec)); -#else - int e, Esize; - - Esize = gp_EdgeIndexBound(context->theGraph); - for (e = gp_GetFirstEdge(context->theGraph); e < Esize; e++) - _K4Search_InitEdgeRec(context, e); -#endif - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_InitGraph(graphP theGraph, int N) -{ - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context == NULL) - return NOTOK; - - theGraph->N = N; - theGraph->NV = N; - if (theGraph->arcCapacity == 0) - theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N; - - if (_K4Search_CreateStructures(context) != OK || - _K4Search_InitStructures(context) != OK) - return NOTOK; - - context->functions.fpInitGraph(theGraph, N); - - return OK; -} - -/******************************************************************** - ********************************************************************/ - -void _K4Search_ReinitializeGraph(graphP theGraph) -{ - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - // Reinitialize the graph - context->functions.fpReinitializeGraph(theGraph); - - // Do the reinitialization that is specific to this module - _K4Search_InitStructures(context); - } -} - -/******************************************************************** - The current implementation does not support an increase of arc - (edge record) capacity once the extension is attached to the graph - data structure. This is only due to not being necessary to support. - For now, it is easy to ensure the correct capacity before attaching - the extension, but support could be added later if there is some - reason to do so. - ********************************************************************/ - -int _K4Search_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ - return NOTOK; -} - -/******************************************************************** - _K4Search_DupContext() - ********************************************************************/ - -void *_K4Search_DupContext(void *pContext, void *theGraph) -{ - K4SearchContext *context = (K4SearchContext *) pContext; - K4SearchContext *newContext = (K4SearchContext *) malloc(sizeof(K4SearchContext)); - - if (newContext != NULL) - { - int Esize = gp_EdgeIndexBound((graphP) theGraph); - - *newContext = *context; - - newContext->theGraph = (graphP) theGraph; - - newContext->initialized = 0; - _K4Search_ClearStructures(newContext); - if (((graphP) theGraph)->N > 0) - { - if (_K4Search_CreateStructures(newContext) != OK) - { - _K4Search_FreeContext(newContext); - return NULL; - } - - memcpy(newContext->E, context->E, Esize*sizeof(K4Search_EdgeRec)); - } - } - - return newContext; -} - -/******************************************************************** - _K4Search_FreeContext() - ********************************************************************/ - -void _K4Search_FreeContext(void *pContext) -{ - K4SearchContext *context = (K4SearchContext *) pContext; - - _K4Search_ClearStructures(context); - free(pContext); -} - -/******************************************************************** - ********************************************************************/ - -void _K4Search_InitEdgeRec(K4SearchContext *context, int e) -{ - context->E[e].pathConnector = NIL; -} - -/******************************************************************** - _K4Search_HandleBlockedBicomp() - Returns OK if no K4 homeomorph found and blockage cleared (OK to - proceed with Walkdown embedding) - NONEMBEDDABLE if K4 homeomorph found, and Walkdown embedding - should be terminated. - NOTOK on internal error - ********************************************************************/ - -int _K4Search_HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R) -{ - K4SearchContext *context = NULL; - - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - if (context == NULL) - return NOTOK; - - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - int RetVal = OK; - - // If invoked on a descendant bicomp, then we push its root then search once - // since not finding a K4 homeomorph will also clear the blockage and allow - // the Walkdown to continue walking down - if (R != RootVertex) - { - sp_Push2(theGraph->theStack, R, 0); - if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, R)) == OK) - { - // If the Walkdown will be told it is OK to continue, then we have to take the descendant - // bicomp root back off the stack so the Walkdown can try to descend to it again. - // The top of stack has the pair R and 0/1 direction Walkdown traversal proceeds from R - // Need only R, so pop and discard the direction, then pop R - sp_Pop2_Discard1(theGraph->theStack, R); - - // And we have to clear the indicator of the minor A that was reduced, since it was eliminated. - theGraph->IC.minorType = 0; - } - } - - // Otherwise, if invoked on a child bicomp rooted by a virtual copy of v, - // then we search for a K4 homeomorph, and if OK is returned, then that indicates - // the blockage has been cleared and it is OK to Walkdown the bicomp. - // But the Walkdown finished, already, so we launch it again. - // If the Walkdown returns OK then all forward arcs were embedded. If NONEMBEDDABLE - // is returned, then the bicomp got blocked again, so we have to reiterate the K4 search - else - { - // If Walkdown has recursively called this handler on the bicomp rooted by RootVertex, - // then it is still blocked, so we just return NONEMBEDDABLE, which causes Walkdown to - // return to the loop below and signal that the loop should invoke the Walkdown again. - if (context->handlingBlockedBicomp) - return NONEMBEDDABLE; - - context->handlingBlockedBicomp = TRUE; - do { - // Detect whether bicomp can be used to find a K4 homeomorph. It it does, then - // it returns NONEMBEDDABLE so we break the search because we found the desired K4 - // If OK is returned, then the blockage was cleared and it is OK to Walkdown again. - if ((RetVal = _SearchForK4InBicomp(theGraph, context, v, RootVertex)) != OK) - break; - - // Walkdown again to embed more edges. If Walkdown returns OK, then all remaining - // edges to its descendants are embedded, so we'll get out of this loop. If Walkdown - // detects that it still has not embedded all the edges to descendants of the bicomp's - // root edge child, then Walkdown calls this routine again, and the above non-reentrancy - // code returns NONEMBEDDABLE, causing this loop to search again for a K4. - theGraph->IC.minorType = 0; - RetVal = theGraph->functions.fpWalkDown(theGraph, v, RootVertex); - - // Except if the Walkdown returns NONEMBEDDABLE due to finding a K4 homeomorph entangled - // with a descendant bicomp (the R != RootVertex case above), then it was found - // entangled with Minor A, so we can stop the search if minor A is detected - if (theGraph->IC.minorType & MINORTYPE_A) - break; - - } while (RetVal == NONEMBEDDABLE); - context->handlingBlockedBicomp = FALSE; - } - - return RetVal; - } - else - { - return context->functions.fpHandleBlockedBicomp(theGraph, v, RootVertex, R); - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult) -{ - // For K4 search, we just return the edge embedding result because the - // search result has been obtained already. - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - return edgeEmbeddingResult; - } - - // When not searching for K4, we let the superclass do the work - else - { - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpEmbedPostprocess(theGraph, v, edgeEmbeddingResult); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) -{ - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - return OK; - } - - // When not searching for K4, we let the superclass do the work - else - { - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} - -/******************************************************************** - ********************************************************************/ - -int _K4Search_CheckObstructionIntegrity(graphP theGraph, graphP origGraph) -{ - // When searching for K4, we ensure that theGraph is a subgraph of - // the original graph and that it contains a K4 homeomorph - if (theGraph->embedFlags == EMBEDFLAGS_SEARCHFORK4) - { - int degrees[4], imageVerts[4]; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - return NOTOK; - - if (_getImageVertices(theGraph, degrees, 3, imageVerts, 4) != OK) - return NOTOK; - - if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE) - { - return OK; - } - - return NOTOK; - } - - // When not searching for K4, we let the superclass do the work - else - { - K4SearchContext *context = NULL; - gp_FindExtension(theGraph, K4SEARCH_ID, (void *)&context); - - if (context != NULL) - { - return context->functions.fpCheckObstructionIntegrity(theGraph, origGraph); - } - } - - return NOTOK; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphNonplanar.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphNonplanar.c deleted file mode 100644 index ca8b659cb..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphNonplanar.c +++ /dev/null @@ -1,724 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#define GRAPHNONPLANAR_C - -#include "graph.h" - -/* Imported functions */ - -extern void _InitIsolatorContext(graphP theGraph); -extern int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -extern int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); -extern int _HideInternalEdges(graphP theGraph, int vertex); -extern int _RestoreInternalEdges(graphP theGraph, int stackBottom); - -//extern int _OrientVerticesInEmbedding(graphP theGraph); -extern int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); - -/* Private functions (exported to system) */ - -int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R); -int _InitializeNonplanarityContext(graphP theGraph, int v, int R); - -int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink); -void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY); -int _FindPertinentVertex(graphP theGraph); -int _SetVertexTypesForMarkingXYPath(graphP theGraph); - -int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom); - -int _MarkHighestXYPath(graphP theGraph); -int _MarkZtoRPath(graphP theGraph); -int _FindFuturePertinenceBelowXYPath(graphP theGraph); - -/**************************************************************************** - _ChooseTypeOfNonplanarityMinor() - ****************************************************************************/ - -int _ChooseTypeOfNonplanarityMinor(graphP theGraph, int v, int R) -{ -int W, Px, Py, Z; - -/* Create the initial non-planarity minor state in the isolator context */ - - if (_InitializeNonplanarityContext(theGraph, v, R) != OK) - return NOTOK; - - R = theGraph->IC.r; - W = theGraph->IC.w; - -/* If the root copy is not a root copy of the current vertex v, - then the Walkdown terminated because it couldn't find - a viable path along a child bicomp, which is Minor A. */ - - if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) - { - theGraph->IC.minorType |= MINORTYPE_A; - return OK; - } - -/* If W has a pertinent and future pertinent child bicomp, then we've found Minor B */ - - if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) - { - if (gp_GetVertexLowpoint(theGraph, gp_GetVertexLastPertinentRootChild(theGraph, W)) < v) - { - theGraph->IC.minorType |= MINORTYPE_B; - return OK; - } - } - -/* Find the highest obstructing X-Y path */ - - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - - Px = theGraph->IC.px; - Py = theGraph->IC.py; - -/* If either point of attachment is 'high' (P_x closer to R than X - or P_y closer to R than Y along external face), then we've - matched Minor C. */ - - if (gp_GetVertexObstructionType(theGraph, Px) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW || - gp_GetVertexObstructionType(theGraph, Py) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW) - { - theGraph->IC.minorType |= MINORTYPE_C; - return OK; - } - -/* For Minor D, we search for a path from an internal - vertex Z along the X-Y path up to the root R of the bicomp. */ - - if (_MarkZtoRPath(theGraph) != OK) - return NOTOK; - - if (gp_IsVertex(theGraph->IC.z)) - { - theGraph->IC.minorType |= MINORTYPE_D; - return OK; - } - -/* For Minor E, we search for an future pertinent vertex Z - below the points of attachment of the X-Y path */ - - Z = _FindFuturePertinenceBelowXYPath(theGraph); - if (gp_IsVertex(Z)) - { - theGraph->IC.z = Z; - theGraph->IC.minorType |= MINORTYPE_E; - return OK; - } - - return NOTOK; -} - -/**************************************************************************** - _InitializeNonplanarityContext() - - This method finds the stopping vertices X and Y, and the pertinent vertex W - of a bicomp rooted by vertex R. - - If R is NIL, the routine first determines which bicomp produced non-planarity - condition. If the stack is non-empty, then R is on the top of the stack. - Otherwise, an unembedded fwdArc from the fwdArcList of vertex v is used in - combination with the sortedDFSChildList of v to determine R. - - If the parameter R was not NIL, then this method assumes it must operate - only on the bicomp rooted by R, and it also assumes that the caller has - not cleared the visited flags in the bicomp, so they are cleared. - - This routine imparts consistent orientation to all vertices in bicomp R - since several subroutines count on this. The edge signs are preserved so that - the original orientations of all vertices can be restored. If the vertices - of the embedding are already consistently oriented, then this operation - simply has no effect. - - Finally, in the bicomp R, the vertex types of all non-root vertices on the - external face are classified according to whether or not they are closer to - the root R than X and Y along the external face paths (R X W) and (R Y W). - ****************************************************************************/ - -int _InitializeNonplanarityContext(graphP theGraph, int v, int R) -{ - // Blank out the isolator context, then assign the input graph reference - // and the current vertext v into the context. - _InitIsolatorContext(theGraph); - theGraph->IC.v = v; - - // The bicomp root provided was the one on which the WalkDown was performed, - // but in the case of Minor A, the central bicomp of the minor is at the top - // of the stack, so R must be changed to that value. - if (sp_NonEmpty(theGraph->theStack)) - { - // The top of stack has the pair R and 0/1 direction Walkdown traversal proceeds from R - // Need only R, so pop and discard the direction, then pop R - sp_Pop2_Discard1(theGraph->theStack, R); - } - - theGraph->IC.r = R; - - // A number of subroutines require the main bicomp of the minor to be - // consistently oriented and its visited flags clear. - if (_OrientVerticesInBicomp(theGraph, R, 1) != OK) { - return NOTOK; - } - - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - - // Now we find the active vertices along both external face paths - // extending from R. - _FindActiveVertices(theGraph, R, &theGraph->IC.x, &theGraph->IC.y); - - // Now, we obtain the pertinent vertex W on the lower external face - // path between X and Y (that path that does not include R). - theGraph->IC.w = _FindPertinentVertex(theGraph); - - // Now we can classify the vertices along the external face of the bicomp - // rooted at R as 'high RXW', 'low RXW', 'high RXY', 'low RXY' - if (_SetVertexTypesForMarkingXYPath(theGraph) != OK) - return NOTOK; - - // All work is done, so return success - return OK; -} - -/******************************************************************** - _GetNeighborOnExtFace() - - Each vertex contains two 'link' index pointers that indicate the - first and last adjacency list arc. If the vertex is on the external face, - then these two arcs are also on the external face. We want to take one of - those edges to get to the next vertex on the external face. - On input *pPrevLink indicates which link we followed to arrive at - curVertex. On output *pPrevLink will be set to the link we follow to - get into the next vertex. - To get to the next vertex, we use the opposite link from the one used - to get into curVertex. This takes us to an edge node. The twinArc - of that edge node, carries us to an edge node in the next vertex. - At least one of the two links in that edge node will lead to a vertex - node in G, which is the next vertex. Once we arrive at the next - vertex, at least one of its links will lead back to the edge node, and - that link becomes the output value of *pPrevLink. - - NOTE: This method intentionally ignores the extFace optimization - links. It is invoked when the "real" external face must be - traversed and hence when the constant time guarantee is not - needed from the extFace short-circuit that connects the - bicomp root to the first active vertices along each external - face path emanating from the bicomp root. - ********************************************************************/ - -int _GetNeighborOnExtFace(graphP theGraph, int curVertex, int *pPrevLink) -{ - /* Exit curVertex from whichever link was not previously used to enter it */ - - int arc = gp_GetArc(theGraph, curVertex, 1^(*pPrevLink)); - int nextVertex = gp_GetNeighbor(theGraph, arc); - - /* This if stmt assigns the new prev link that tells us which edge - record was used to enter nextVertex (so that we exit from the - opposing edge record). - - However, if we are in a singleton bicomp, then both links in nextVertex - lead back to curVertex. We want the two arcs of a singleton bicomp to - act like a cycle, so we just don't change the prev link in this case. - - But when nextVertex has more than one edge, we need to figure out - whether the first edge or last edge (which are the two on the external - face) was used to enter nextVertex so we can exit from the other one - as traversal of the external face continues later. */ - - if (gp_GetFirstArc(theGraph, nextVertex) != gp_GetLastArc(theGraph, nextVertex)) - *pPrevLink = gp_GetTwinArc(theGraph, arc) == gp_GetFirstArc(theGraph, nextVertex) ? 0 : 1; - - return nextVertex; -} - -/**************************************************************************** - _FindActiveVertices() - - Descends from the root of a bicomp R along both external face paths (which - are indicated by the first and last arcs in R's adjacency list), returning - the first active vertex appearing in each direction. - ****************************************************************************/ - -void _FindActiveVertices(graphP theGraph, int R, int *pX, int *pY) -{ -int XPrevLink=1, YPrevLink=0, v=theGraph->IC.v; - - *pX = _GetNeighborOnExtFace(theGraph, R, &XPrevLink); - *pY = _GetNeighborOnExtFace(theGraph, R, &YPrevLink); - - // For planarity algorithms, advance past inactive vertices - // For outerplanarity algorithms, ignore the notion of inactive vertices - // since all vertices must remain on the external face. - if (!(theGraph->embedFlags & EMBEDFLAGS_OUTERPLANAR)) - { - gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v); - while (INACTIVE(theGraph, *pX, v)) - { - *pX = _GetNeighborOnExtFace(theGraph, *pX, &XPrevLink); - gp_UpdateVertexFuturePertinentChild(theGraph, *pX, v); - } - - gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v); - while (INACTIVE(theGraph, *pY, v)) - { - *pY = _GetNeighborOnExtFace(theGraph, *pY, &YPrevLink); - gp_UpdateVertexFuturePertinentChild(theGraph, *pY, v); - } - } -} - -/**************************************************************************** - _FindPertinentVertex() - - Get the first vertex after x. Since x was obtained using a prevlink of 1 on r, - we use the same prevlink so we don't go back to R. - Then, we proceed around the lower path until we find a vertex W that either - has pertinent child bicomps or is directly adjacent to the current vertex v. - ****************************************************************************/ - -int _FindPertinentVertex(graphP theGraph) -{ -int W=theGraph->IC.x, WPrevLink=1; - - W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); - - while (W != theGraph->IC.y) - { - if (PERTINENT(theGraph, W)) - return W; - - W = _GetNeighborOnExtFace(theGraph, W, &WPrevLink); - } - - return NIL; -} - -/**************************************************************************** - _SetVertexTypesForMarkingXYPath() - - Label the vertices along the external face of the bicomp rooted at R as - 'high RXW', 'low RXW', 'high RXY', 'low RXY' - ****************************************************************************/ - -int _SetVertexTypesForMarkingXYPath(graphP theGraph) -{ - int R, X, Y, W, Z, ZPrevLink, ZType; - - // Unpack the context for efficiency of loops - R = theGraph->IC.r; - X = theGraph->IC.x; - Y = theGraph->IC.y; - W = theGraph->IC.w; - - // Ensure basic preconditions of this routine are met - if (gp_IsNotVertex(R) || gp_IsNotVertex(X) || gp_IsNotVertex(Y) || gp_IsNotVertex(W)) - return NOTOK; - - // Clear the type member of each vertex in the bicomp - if (_ClearVertexTypeInBicomp(theGraph, R) != OK) - return NOTOK; - - // Traverse from R to W in the X direction - ZPrevLink = 1; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RXW; - while (Z != W) - { - if (Z == X) ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RXW; - gp_ResetVertexObstructionType(theGraph, Z, ZType); - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - // Traverse from R to W in the Y direction - ZPrevLink = 0; - Z = _GetNeighborOnExtFace(theGraph, R, &ZPrevLink); - ZType = VERTEX_OBSTRUCTIONTYPE_HIGH_RYW; - while (Z != W) - { - if (Z == Y) ZType = VERTEX_OBSTRUCTIONTYPE_LOW_RYW; - gp_ResetVertexObstructionType(theGraph, Z, ZType); - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - return OK; -} - -/**************************************************************************** - _PopAndUnmarkVerticesAndEdges() - - Pop all vertex/edge pairs from the top of the stack up to a terminating - vertex Z and mark as unvisited. If Z is NIL, then all vertex/edge pairs - are popped and marked as unvisited. - The stackBottom indicates where other material besides the vertex/edge - pairs may appear. - ****************************************************************************/ - -int _PopAndUnmarkVerticesAndEdges(graphP theGraph, int Z, int stackBottom) -{ -int V, e; - - // Pop vertex/edge pairs until all have been popped from the stack, - // and all that's left is what was under the pairs, or until... - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - // If we pop the terminating vertex Z, then put it back and break - if (V == Z) - { - sp_Push(theGraph->theStack, V); - break; - } - - // Otherwise, pop the edge part of the vertex/edge pair - sp_Pop(theGraph->theStack, e); - - // Now unmark the vertex and edge (i.e. revert to "unvisited") - gp_ClearVertexVisited(theGraph, V); - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - return OK; -} - -/**************************************************************************** - _MarkHighestXYPath() - - An X-Y path in the bicomp rooted by R is a path attached to the external - face at points Px and Py that separates W from R such that a back edge (R, W) - cannot be embedded within the bicomp. Recall that R is a root copy of v, so - (R, W) is the representative of (v, W). Also, note that W is pertinent if - either W *or* one of its descendants in a separate bicomp has, in the input - graph, a back edge to v. - - If no X-Y path separating W from R is found, then NOTOK is returned because - the proof of correctness guarantees that one exists (although this routine - can also be used to help test for the existence of an X-Y path, and NOTOK - means 'no' in that case). - - The desired output is to set the 'visited' flags of the X-Y path with - highest points of attachment to the external face (i.e. the points of - attachment that are closest to R along the external face). This includes - marking both the vertices and edges along the X-Y path. - - Previously, during non-planarity context initialization, the vertices along - the external face (other than R and W) have been classified as 'high RXW', - 'low RXW', 'high RXY', or 'low RXY'. Once the vertices have been categorized, - we proceed with trying to set the visitation flags in the way described above. - First, we remove all edges incident to R except the two edges that join R to - the external face. The result is that R and its two remaining edges are a - 'corner' in the external face but also in a single proper face whose boundary - includes the X-Y path with the highest attachment points. Thus, we simply need - to walk this proper face to find the desired X-Y path. Note, however, that the - resulting face boundary may have attached cut vertices. Any such separable - component contains a vertex neighbor of R, but the edge to R has been - temporarily removed. The algorithm removes loop of vertices and edges along - the proper face so that only a path is identified. - - To walk the proper face containing R, we begin with its first arc successor, - then take the *predecessor* arc at every subsequent corner. For each vertex, - we mark as visited the vertex as well as the edge used to enter the vertex - (except for the edge used to enter the RXW vertex). We also push the visited - vertices and edges onto a stack. - - As we walk the proper face, we keep track of the last vertex P_x we visited of - type RXW (high or low). Each time we encounter a vertex of type RXW (high or - low), we pop the stack and unmark all of the edges and vertices visited because - they were part of a path parallel to the external face that does not obstruct - W from reaching R within the bicomp. If we encounter vertex W, then there is - no obstructing X-Y path since we removed only edges incident to R, so we pop - the stack unmarking everything then return NOTOK as stated above. If we - encounter a vertex Z previously visited, then we pop the stack, unmarking the - vertices and edges popped, until we find the prior occurence of Z on the stack. - - Otherwise, the first time we encounter a vertex P_y of type 'RYW', we stop - because the obstructing X-Y path has been marked visited and its points of - connection P_x and P_y have been found. - - Once the X-Y path is identified, we restore the edges incident to R. - - This method uses the stack, but it preserves any prior content. - The stack space used is no greater than 3N. The first N accounts for removing - the edges incident to R. The other 2N accounts for the fact that each - iteration of the main loop visits a vertex, pushing its index and the - location of an edge record. If a vertex is encountered that is already - on the stack, then it is not pushed again (and in fact part of the stack - is removed). - - Returns TRUE if the X-Y path is found, FALSE otherwise. - In debug mode it can also return NOTOK. This is equivalent to FALSE, but - NOTOK is better for documenting the error condition in the code, and - it produces a debug message. Also, in many cases the equivalent-to-FALSE - result is an error condition for the caller, so NOTOK usually percolates up. - ****************************************************************************/ - -int _MarkHighestXYPath(graphP theGraph) -{ -int e, Z; -int R, W; -int stackBottom1, stackBottom2; - -/* Initialization */ - - R = theGraph->IC.r; - W = theGraph->IC.w; - theGraph->IC.px = theGraph->IC.py = NIL; - -/* Save the stack bottom before we start hiding internal edges, so - we will know how many edges to restore */ - - stackBottom1 = sp_GetCurrentSize(theGraph->theStack); - -/* Remove the internal edges incident to vertex R */ - - if (_HideInternalEdges(theGraph, R) != OK) - return NOTOK; - -/* Now we're going to use the stack to collect the vertices of potential - * X-Y paths, so we need to store where the hidden internal edges are - * located because we must, at times, pop the collected vertices if - * the path being collected doesn't work out. */ - - stackBottom2 = sp_GetCurrentSize(theGraph->theStack); - -/* Walk the proper face containing R to find and mark the highest - X-Y path. Note that if W is encountered, then there is no - intervening X-Y path, so we would return FALSE in that case. */ - - Z = R; - // This setting of e is the arc equivalent of prevLink=1 - // As loop progresses, e indicates the arc used to enter Z, not the exit arc - e = gp_GetLastArc(theGraph, R); - - while (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_HIGH_RYW && - gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - { - /* Advance e and Z along the proper face containing R */ - - e = gp_GetPrevArcCircular(theGraph, e); - Z = gp_GetNeighbor(theGraph, e); - e = gp_GetTwinArc(theGraph, e); - - /* If Z is already visited, then pop everything since the last time - we visited Z because its all part of a separable component. */ - - if (gp_GetVertexVisited(theGraph, Z)) - { - if (_PopAndUnmarkVerticesAndEdges(theGraph, Z, stackBottom2) != OK) - return NOTOK; - } - - /* If we have not visited this vertex before... */ - - else - { - /* If we find W, then there is no X-Y path. Never happens - for Kuratowski subgraph isolator, but this routine is - also used to test for certain X-Y paths. - So, we clean up and bail out in that case. */ - - if (Z == W) - { - if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK) - return NOTOK; - break; - } - - /* If we found another vertex along the RXW path, then blow off - all the vertices we visited so far because they're not part of - the obstructing path */ - - if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RXW || - gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RXW) - { - theGraph->IC.px = Z; - if (_PopAndUnmarkVerticesAndEdges(theGraph, NIL, stackBottom2) != OK) - return NOTOK; - } - - /* Push the current vertex onto the stack of vertices visited - since the last RXW vertex was encountered */ - - sp_Push(theGraph->theStack, e); - sp_Push(theGraph->theStack, Z); - - /* Mark the vertex Z as visited as well as its edge of entry - (except the entry edge for P_x).*/ - - gp_SetVertexVisited(theGraph, Z); - if (Z != theGraph->IC.px) - { - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - } - - /* If we found an RYW vertex, then we have successfully finished - identifying the highest X-Y path, so we record the point of - attachment and break the loop. */ - - if (gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_HIGH_RYW || - gp_GetVertexObstructionType(theGraph, Z) == VERTEX_OBSTRUCTIONTYPE_LOW_RYW) - { - theGraph->IC.py = Z; - break; - } - } - } - -/* Remove any remaining vertex-edge pairs on the top of the stack, then - Restore the internal edges incident to R that were previously removed. */ - - sp_SetCurrentSize(theGraph->theStack, stackBottom2); - - if (_RestoreInternalEdges(theGraph, stackBottom1) != OK) - return NOTOK; - -/* Return the result */ - - return gp_IsVertex(theGraph->IC.py) ? TRUE : FALSE; -} - -/**************************************************************************** - _MarkZtoRPath() - - This function assumes that _MarkHighestXYPath() has already been called, - which marked as visited the vertices and edges along the X-Y path. - - We begin at the point of attachment P_x, take the last arc and traverse - the predecessor arcs until we find one marked visited, which leads to the - first internal vertex along the X-Y path. We begin with this vertex - (and its edge of entry), and we run until we find P_y. For each internal - vertex Z and its edge of entry ZPrevArc, we take the predecessor edge record - of ZPrevArc. This is called ZNextArc. If ZNextArc is marked visited - then it is along the X-Y path, so we use it to exit Z and go to the next - vertex on the X-Y path. - - If ZNextArc is not visited, then when _MarkHighestXYPath() ran, it exited - Z from ZNextArc, then eventually reentered Z. In other words, Z became a - cut vertex when we removed the internal edges incident to R. Thus, ZNextArc - indicates the first edge in an internal path to R. - - When we find an unvisited ZNextArc, we stop running the X-Y path and instead - begin marking the Z to R path. We move to successive vertices using a - twin arc then its predecessor arc in the adjacency list, only this time - we have not removed the internal edges incident to R, so this technique does - eventually lead us all the way to R. - - If we do not find an unvisited ZNextArc for any vertex Z on the X-Y path and - inside the bicomp, then there is no Z to R path, so we return. - ****************************************************************************/ - -int _MarkZtoRPath(graphP theGraph) -{ -int ZPrevArc, ZNextArc, Z, R, Px, Py; - -/* Initialize */ - - R = theGraph->IC.r; - Px = theGraph->IC.px; - Py = theGraph->IC.py; - theGraph->IC.z = NIL; - -/* Begin at Px and search its adjacency list for the edge leading to - the first internal vertex of the X-Y path. */ - - Z = Px; - ZNextArc = gp_GetLastArc(theGraph, Z); - while (ZNextArc != gp_GetFirstArc(theGraph, Z)) - { - if (gp_GetEdgeVisited(theGraph, ZNextArc)) break; - - ZNextArc = gp_GetPrevArc(theGraph, ZNextArc); - } - - if (!gp_GetEdgeVisited(theGraph, ZNextArc)) - return NOTOK; - -/* For each internal vertex Z, determine whether it has a path to root. */ - - while (gp_GetEdgeVisited(theGraph, ZNextArc)) - { - ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); - ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc); - } - - ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); - Z = gp_GetNeighbor(theGraph, ZPrevArc); - -/* If there is no Z to R path, return */ - - if (Z == Py) return OK; - -/* Otherwise, store Z in the isolation context */ - - theGraph->IC.z = Z; - -/* Walk the proper face starting with (Z, ZNextArc) until we reach R, marking - the vertices and edges encountered along the way, then Return OK. */ - - while (Z != R) - { - /* If we ever encounter a non-internal vertex (other than the root R), - then corruption has occured, so we return NOTOK */ - - if (gp_GetVertexObstructionType(theGraph, Z) != VERTEX_OBSTRUCTIONTYPE_UNKNOWN) - return NOTOK; - - /* Go to the next vertex indicated by ZNextArc */ - - Z = gp_GetNeighbor(theGraph, ZNextArc); - - /* Mark the next vertex and the edge leading to it as visited. */ - - gp_SetEdgeVisited(theGraph, ZNextArc); - gp_SetEdgeVisited(theGraph, ZPrevArc); - gp_SetVertexVisited(theGraph, Z); - - /* Go to the next edge in the proper face */ - - ZNextArc = gp_GetPrevArcCircular(theGraph, ZPrevArc); - ZPrevArc = gp_GetTwinArc(theGraph, ZNextArc); - } - -/* Found Z to R path, so indicate as much to caller */ - - return OK; -} - -/**************************************************************************** - _FindFuturePertinenceBelowXYPath() - - Get a future pertinent vertex along the lower external face path between - the points of attachment P_x and P_y of a 'low' X-Y Path. - NOTE: By the time this function is called, Px and Py have already been found - to be at or below X and Y. - ****************************************************************************/ - -int _FindFuturePertinenceBelowXYPath(graphP theGraph) -{ -int Z=theGraph->IC.px, ZPrevLink=1, - Py=theGraph->IC.py, v=theGraph->IC.v; - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - - while (Z != Py) - { - gp_UpdateVertexFuturePertinentChild(theGraph, Z, v); - if (FUTUREPERTINENT(theGraph, Z, v)) - return Z; - - Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink); - } - - return NIL; -} diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphOuterplanarObstruction.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphOuterplanarObstruction.c deleted file mode 100644 index 9839059b0..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphOuterplanarObstruction.c +++ /dev/null @@ -1,212 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "graph.h" - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -/* Imported functions */ - -extern void _ClearVisitedFlags(graphP); - -extern int _JoinBicomps(graphP theGraph); - -extern int _InitializeNonplanarityContext(graphP theGraph, int v, int R); -extern int _MarkHighestXYPath(graphP theGraph); - -//extern int _FindUnembeddedEdgeToAncestor(graphP theGraph, int cutVertex, int *pAncestor, int *pDescendant); -extern int _FindUnembeddedEdgeToCurVertex(graphP theGraph, int cutVertex, int *pDescendant); -extern int _FindUnembeddedEdgeToSubtree(graphP theGraph, int ancestor, int SubtreeRoot, int *pDescendant); - -extern int _MarkPathAlongBicompExtFace(graphP theGraph, int startVert, int endVert); - -extern int _AddAndMarkEdge(graphP theGraph, int ancestor, int descendant); - -extern int _DeleteUnmarkedVerticesAndEdges(graphP theGraph); - -/* Private function declarations (exported to system) */ - -int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R); - -int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R); - -int _IsolateOuterplanarityObstructionA(graphP theGraph); -int _IsolateOuterplanarityObstructionB(graphP theGraph); -int _IsolateOuterplanarityObstructionE(graphP theGraph); - -/**************************************************************************** - _ChooseTypeOfNonOuterplanarityMinor() - A constant time implementation is easily feasible but only constant amortized - time is needed for the outerplanarity obstruction isolation, which also - benefits from having the bicomp rooted by R oriented. - If an extension algorithm requires constant actual time, then this function - should not be used and instead the minor should be decided without orienting - the bicomp. - ****************************************************************************/ - -int _ChooseTypeOfNonOuterplanarityMinor(graphP theGraph, int v, int R) -{ -int W; - - // Create the initial non-outerplanarity obstruction isolator state. - if (_InitializeNonplanarityContext(theGraph, v, R) != OK) - return NOTOK; - - R = theGraph->IC.r; - W = theGraph->IC.w; - - // If the root copy is not a root copy of the current vertex v, - // then the Walkdown terminated on a descendant bicomp, which is Minor A. - if (gp_GetPrimaryVertexFromRoot(theGraph, R) != v) - { - theGraph->IC.minorType |= MINORTYPE_A; - return OK; - } - - // If W has a pertinent child bicomp, then we've found Minor B. - // Notice this is different from planarity, in which minor B is indicated - // only if the pertinent child bicomp is also future pertinent. - if (gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, W))) - { - theGraph->IC.minorType |= MINORTYPE_B; - return OK; - } - - // The only other result is minor E (we will search for the X-Y path later) - theGraph->IC.minorType |= MINORTYPE_E; - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarObstruction() - ****************************************************************************/ - -int _IsolateOuterplanarObstruction(graphP theGraph, int v, int R) -{ -int RetVal; - -/* A subgraph homeomorphic to K_{2,3} or K_4 will be isolated by using the visited - flags, set=keep edge/vertex and clear=omit. Here we initialize to omit all, then we - subsequently set visited on all edges and vertices in the homeomorph. */ - - _ClearVisitedFlags(theGraph); - -/* Next we determineg which of the non-outerplanarity Minors was encountered - and the principal bicomp on which the isolator will focus attention. */ - - if (_ChooseTypeOfNonOuterplanarityMinor(theGraph, v, R) != OK) - return NOTOK; - -/* Find the path connecting the pertinent vertex w with the current vertex v */ - - if (theGraph->IC.minorType & MINORTYPE_B) - { - isolatorContextP IC = &theGraph->IC; - int SubtreeRoot = gp_GetVertexLastPertinentRootChild(theGraph, IC->w); - - if (_FindUnembeddedEdgeToSubtree(theGraph, IC->v, SubtreeRoot, &IC->dw) != TRUE) - return NOTOK; - } - else - { - isolatorContextP IC = &theGraph->IC; - - if (_FindUnembeddedEdgeToCurVertex(theGraph, IC->w, &IC->dw) != TRUE) - return NOTOK; - } - -/* For minor E, we need to find and mark an X-Y path */ - - if (theGraph->IC.minorType & MINORTYPE_E) - { - if (_MarkHighestXYPath(theGraph) != TRUE) - return NOTOK; - } - -/* Call the appropriate isolator */ - - if (theGraph->IC.minorType & MINORTYPE_A) - RetVal = _IsolateOuterplanarityObstructionA(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_B) - RetVal = _IsolateOuterplanarityObstructionB(theGraph); - else if (theGraph->IC.minorType & MINORTYPE_E) - RetVal = _IsolateOuterplanarityObstructionE(theGraph); - else - RetVal = NOTOK; - -/* Delete the unmarked edges and vertices, and return */ - - if (RetVal == OK) - RetVal = _DeleteUnmarkedVerticesAndEdges(theGraph); - - return RetVal; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionA(): Isolate a K2,3 homeomorph - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionA(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->v, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionB(): Isolate a K2,3 homeomorph - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionB(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} - -/**************************************************************************** - _IsolateOuterplanarityObstructionE(): Isolate a K4 homeomorph - ****************************************************************************/ - -int _IsolateOuterplanarityObstructionE(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - if (_MarkPathAlongBicompExtFace(theGraph, IC->r, IC->r) != OK || - theGraph->functions.fpMarkDFSPath(theGraph, IC->w, IC->dw) != OK || - _JoinBicomps(theGraph) != OK || - _AddAndMarkEdge(theGraph, IC->v, IC->dw) != OK) - return NOTOK; - - return OK; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphStructures.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphStructures.h deleted file mode 100644 index 9b1b51536..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphStructures.h +++ /dev/null @@ -1,794 +0,0 @@ -#ifndef GRAPHSTRUCTURE_H -#define GRAPHSTRUCTURE_H - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include -#include "appconst.h" -#include "listcoll.h" -#include "stack.h" - -#include "graphFunctionTable.h" -#include "graphExtensions.private.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// A return value to indicate success prior to completely processing a graph, whereas -// OK signifies EMBEDDABLE (no unreducible obstructions) and NOTOK signifies an exception. -#define NONEMBEDDABLE -1 - -// The initial setting for the edge storage capacity expressed as a constant factor of N, -// which is the number of vertices in the graph. By default, array E is allocated enough -// space to contain 3N edges, which is 6N arcs (half edges), but this initial setting -// can be overridden using gp_EnsureArcCapacity(), which is especially efficient if done -// before calling gp_InitGraph() or gp_Read(). -#define DEFAULT_EDGE_LIMIT 3 - -/******************************************************************** - Edge Record Definition - - An edge is defined by a pair of edge records, or arcs, allocated in - array E of a graph. An edge record represents the edge in the - adjacency list of each vertex to which the edge is incident. - - link[2]: the next and previous edge records (arcs) in the adjacency - list that contains this edge record. - - v: The vertex neighbor of the vertex whose adjacency list contains - this edge record (an index into array V). - - flags: Bits 0-15 reserved for library; bits 16 and higher for apps - Bit 0: Visited - Bit 1: DFS type has been set, versus not set - Bit 2: DFS tree edge, versus cycle edge (co-tree edge, etc.) - Bit 3: DFS arc to descendant, versus arc to ancestor - Bit 4: Inverted (same as marking an edge with a "sign" of -1) - Bit 5: Arc is directed into the containing vertex only - Bit 6: Arc is directed from the containing vertex only - ********************************************************************/ - -typedef struct -{ - int link[2]; - int neighbor; - unsigned flags; -} edgeRec; - -typedef edgeRec * edgeRecP; - -#if NIL == 0 -#define gp_IsArc(e) (e) -#define gp_IsNotArc(e) (!(e)) -#define gp_GetFirstEdge(theGraph) (2) -#elif NIL == -1 -#define gp_IsArc(e) ((e) != NIL) -#define gp_IsNotArc(e) ((e) == NIL) -#define gp_GetFirstEdge(theGraph) (0) -#else -#error NIL must be 0 or -1 -#endif - -#define gp_EdgeInUse(theGraph, e) (gp_IsVertex(gp_GetNeighbor(theGraph, e))) -#define gp_EdgeNotInUse(theGraph, e) (gp_IsNotVertex(gp_GetNeighbor(theGraph, e))) -#define gp_EdgeIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (theGraph)->arcCapacity) -#define gp_EdgeInUseIndexBound(theGraph) (gp_GetFirstEdge(theGraph) + (((theGraph)->M + sp_GetCurrentSize((theGraph)->edgeHoles)) << 1)) - -// An edge is represented by two consecutive edge records (arcs) in the edge array E. -// If an even number, xor 1 will add one; if an odd number, xor 1 will subtract 1 -#define gp_GetTwinArc(theGraph, Arc) ((Arc) ^ 1) - -// Access to adjacency list pointers -#define gp_GetNextArc(theGraph, e) (theGraph->E[e].link[0]) -#define gp_GetPrevArc(theGraph, e) (theGraph->E[e].link[1]) -#define gp_GetAdjacentArc(theGraph, e, theLink) (theGraph->E[e].link[theLink]) - -#define gp_SetNextArc(theGraph, e, newNextArc) (theGraph->E[e].link[0] = newNextArc) -#define gp_SetPrevArc(theGraph, e, newPrevArc) (theGraph->E[e].link[1] = newPrevArc) -#define gp_SetAdjacentArc(theGraph, e, theLink, newArc) (theGraph->E[e].link[theLink] = newArc) - -// Access to vertex 'neighbor' member indicated by arc -#define gp_GetNeighbor(theGraph, e) (theGraph->E[e].neighbor) -#define gp_SetNeighbor(theGraph, e, v) (theGraph->E[e].neighbor = v) - -// Initializer for edge flags -#define gp_InitEdgeFlags(theGraph, e) (theGraph->E[e].flags = 0) - -// Definitions of and access to edge flags -#define EDGE_VISITED_MASK 1 -#define gp_GetEdgeVisited(theGraph, e) (theGraph->E[e].flags&EDGE_VISITED_MASK) -#define gp_ClearEdgeVisited(theGraph, e) (theGraph->E[e].flags &= ~EDGE_VISITED_MASK) -#define gp_SetEdgeVisited(theGraph, e) (theGraph->E[e].flags |= EDGE_VISITED_MASK) - -// The edge type is defined by bits 1-3, 2+4+8=14 -#define EDGE_TYPE_MASK 14 - -// Call gp_GetEdgeType(), then compare to one of these four possibilities -// EDGE_TYPE_CHILD - edge record is an arc to a DFS child -// EDGE_TYPE_FORWARD - edge record is an arc to a DFS descendant, not a DFS child -// EDGE_TYPE_PARENT - edge record is an arc to the DFS parent -// EDGE_TYPE_BACK - edge record is an arc to a DFS ancestor, not the DFS parent -#define EDGE_TYPE_CHILD 14 -#define EDGE_TYPE_FORWARD 10 -#define EDGE_TYPE_PARENT 6 -#define EDGE_TYPE_BACK 2 - -// EDGE_TYPE_NOTDEFINED - the edge record type has not been defined -// EDGE_TYPE_RANDOMTREE - edge record is part of a randomly generated tree -#define EDGE_TYPE_NOTDEFINED 0 -#define EDGE_TYPE_RANDOMTREE 4 - -#define gp_GetEdgeType(theGraph, e) (theGraph->E[e].flags&EDGE_TYPE_MASK) -#define gp_ClearEdgeType(theGraph, e) (theGraph->E[e].flags &= ~EDGE_TYPE_MASK) -#define gp_SetEdgeType(theGraph, e, type) (theGraph->E[e].flags |= type) -#define gp_ResetEdgeType(theGraph, e, type) \ - (theGraph->E[e].flags = (theGraph->E[e].flags & ~EDGE_TYPE_MASK) | type) - -#define EDGEFLAG_INVERTED_MASK 16 -#define gp_GetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_INVERTED_MASK) -#define gp_SetEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags |= EDGEFLAG_INVERTED_MASK) -#define gp_ClearEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags &= (~EDGEFLAG_INVERTED_MASK)) -#define gp_XorEdgeFlagInverted(theGraph, e) (theGraph->E[e].flags ^= EDGEFLAG_INVERTED_MASK) - -#define EDGEFLAG_DIRECTION_INONLY 32 -#define EDGEFLAG_DIRECTION_OUTONLY 64 -#define EDGEFLAG_DIRECTION_MASK 96 - -// Returns the direction, if any, of the edge record -#define gp_GetDirection(theGraph, e) (theGraph->E[e].flags & EDGEFLAG_DIRECTION_MASK) - -//A direction of 0 clears directedness. Otherwise, edge record e is set -//to edgeFlag_Direction and e's twin arc is set to the opposing setting. -#define gp_SetDirection(theGraph, e, edgeFlag_Direction) \ -{ \ - if (edgeFlag_Direction == EDGEFLAG_DIRECTION_INONLY) \ - { \ - theGraph->E[e].flags |= EDGEFLAG_DIRECTION_INONLY; \ - theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_OUTONLY; \ - } \ - else if (edgeFlag_Direction == EDGEFLAG_DIRECTION_OUTONLY) \ - { \ - theGraph->E[e].flags |= EDGEFLAG_DIRECTION_OUTONLY; \ - theGraph->E[gp_GetTwinArc(theGraph, e)].flags |= EDGEFLAG_DIRECTION_INONLY; \ - } \ - else \ - { \ - theGraph->E[e].flags &= ~(EDGEFLAG_DIRECTION_INONLY|EDGEFLAG_DIRECTION_OUTONLY); \ - theGraph->E[gp_GetTwinArc(theGraph, e)].flags &= ~EDGEFLAG_DIRECTION_MASK; \ - } \ -} - -#define gp_CopyEdgeRec(dstGraph, edst, srcGraph, esrc) (dstGraph->E[edst] = srcGraph->E[esrc]) - -/******************************************************************** - Vertex Record Definition - - This record definition provides the data members needed for the - core structural information for both vertices and virtual vertices. - Vertices are also equipped with additional information provided by - the vertexInfo structure. - - The vertices of a graph are stored in the first N locations of array V. - Virtual vertices are secondary vertices used to help represent the - main vertices in substructural components of a graph (e.g. biconnected - components). - - link[2]: the first and last edge records (arcs) in the adjacency list - of the vertex. - - index: In vertices, stores either the depth first index of a vertex or - the original array index of the vertex if the vertices of the - graph are sorted by DFI. - In virtual vertices, the index may be used to indicate the vertex - that the virtual vertex represents, unless an algorithm has some - other way of making the association (for example, the planarity - algorithms rely on biconnected components and therefore place - virtual vertices of a vertex at positions corresponding to the - DFS children of the vertex). - - flags: Bits 0-15 reserved for library; bits 16 and higher for apps - Bit 0: visited, for vertices and virtual vertices - Use in lieu of TYPE_VERTEX_VISITED in K4 algorithm - Bit 1: Obstruction type VERTEX_TYPE_SET (versus not set, i.e. VERTEX_TYPE_UNKNOWN) - Bit 2: Obstruction type qualifier RYW (set) versus RXW (clear) - Bit 3: Obstruction type qualifier high (set) versus low (clear) - ********************************************************************/ - -typedef struct -{ - int link[2]; - int index; - unsigned flags; -} vertexRec; - -typedef vertexRec * vertexRecP; - -// Accessors for vertex adjacency list links -#define gp_GetFirstArc(theGraph, v) (theGraph->V[v].link[0]) -#define gp_GetLastArc(theGraph, v) (theGraph->V[v].link[1]) -#define gp_GetArc(theGraph, v, theLink) (theGraph->V[v].link[theLink]) - -#define gp_SetFirstArc(theGraph, v, newFirstArc) (theGraph->V[v].link[0] = newFirstArc) -#define gp_SetLastArc(theGraph, v, newLastArc) (theGraph->V[v].link[1] = newLastArc) -#define gp_SetArc(theGraph, v, theLink, newArc) (theGraph->V[v].link[theLink] = newArc) - -// Vertex conversions and iteration -#if NIL == 0 -#define gp_IsVertex(v) (v) -#define gp_IsNotVertex(v) (!(v)) - -#define gp_GetFirstVertex(theGraph) (1) -#define gp_GetLastVertex(theGraph) ((theGraph)->N) -#define gp_VertexInRange(theGraph, v) ((v) <= (theGraph)->N) -#define gp_VertexInRangeDescending(theGraph, v) (v) - -#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N) -#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N) - -#define gp_IsVirtualVertex(theGraph, v) ((v) > theGraph->N) -#define gp_IsNotVirtualVertex(theGraph, v) ((v) <= theGraph->N) -#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N + 1) -#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV) -#define gp_VirtualVertexInRange(theGraph, v) ((v) <= theGraph->N + theGraph->NV) - -#elif NIL == -1 -#define gp_IsVertex(v) ((v) != NIL) -#define gp_IsNotVertex(v) ((v) == NIL) - -#define gp_GetFirstVertex(theGraph) (0) -#define gp_GetLastVertex(theGraph) ((theGraph)->N - 1) -#define gp_VertexInRange(theGraph, v) ((v) < (theGraph)->N) -#define gp_VertexInRangeDescending(theGraph, v) ((v) >= 0) - -#define gp_PrimaryVertexIndexBound(theGraph) (gp_GetFirstVertex(theGraph) + (theGraph)->N) -#define gp_VertexIndexBound(theGraph) (gp_PrimaryVertexIndexBound(theGraph) + (theGraph)->N) - -#define gp_IsVirtualVertex(theGraph, v) ((v) >= theGraph->N) -#define gp_IsNotVirtualVertex(theGraph, v) ((v) < theGraph->N) -#define gp_VirtualVertexInUse(theGraph, virtualVertex) (gp_IsArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_VirtualVertexNotInUse(theGraph, virtualVertex) (gp_IsNotArc(gp_GetFirstArc(theGraph, virtualVertex))) -#define gp_GetFirstVirtualVertex(theGraph) (theGraph->N) -#define gp_GetLastVirtualVertex(theGraph) (theGraph->N + theGraph->NV - 1) -#define gp_VirtualVertexInRange(theGraph, v) ((v) < theGraph->N + theGraph->NV) - -#else -#error NIL must be 0 or -1 -#endif - -#define gp_GetRootFromDFSChild(theGraph, c) ((c) + theGraph->N) -#define gp_GetDFSChildFromRoot(theGraph, R) ((R) - theGraph->N) -#define gp_GetPrimaryVertexFromRoot(theGraph, R) gp_GetVertexParent(theGraph, gp_GetDFSChildFromRoot(theGraph, R)) - -#define gp_IsSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild))) -#define gp_IsNotSeparatedDFSChild(theGraph, theChild) (gp_VirtualVertexNotInUse(theGraph, gp_GetRootFromDFSChild(theGraph, theChild))) - -#define gp_IsDFSTreeRoot(theGraph, v) gp_IsNotVertex(gp_GetVertexParent(theGraph, v)) -#define gp_IsNotDFSTreeRoot(theGraph, v) gp_IsVertex(gp_GetVertexParent(theGraph, v)) - -// Accessors for vertex index -#define gp_GetVertexIndex(theGraph, v) (theGraph->V[v].index) -#define gp_SetVertexIndex(theGraph, v, theIndex) (theGraph->V[v].index = theIndex) - -// Initializer for vertex flags -#define gp_InitVertexFlags(theGraph, v) (theGraph->V[v].flags = 0) - -// Definitions and accessors for vertex flags -#define VERTEX_VISITED_MASK 1 -#define gp_GetVertexVisited(theGraph, v) (theGraph->V[v].flags&VERTEX_VISITED_MASK) -#define gp_ClearVertexVisited(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_VISITED_MASK) -#define gp_SetVertexVisited(theGraph, v) (theGraph->V[v].flags |= VERTEX_VISITED_MASK) - -// The obstruction type is defined by bits 1-3, 2+4+8=14 -// Bit 1 - 2 if type set, 0 if not -// Bit 2 - 4 if Y side, 0 if X side -// Bit 3 - 8 if high, 0 if low -#define VERTEX_OBSTRUCTIONTYPE_MASK 14 - -// Call gp_GetVertexObstructionType, then compare to one of these four possibilities -// VERTEX_OBSTRUCTIONTYPE_HIGH_RXW - On the external face path between vertices R and X -// VERTEX_OBSTRUCTIONTYPE_LOW_RXW - X or on the external face path between vertices X and W -// VERTEX_OBSTRUCTIONTYPE_HIGH_RYW - On the external face path between vertices R and Y -// VERTEX_OBSTRUCTIONTYPE_LOW_RYW - Y or on the external face path between vertices Y and W -// VERTEX_OBSTRUCTIONTYPE_UNKNOWN - corresponds to all three bits off -#define VERTEX_OBSTRUCTIONTYPE_HIGH_RXW 10 -#define VERTEX_OBSTRUCTIONTYPE_LOW_RXW 2 -#define VERTEX_OBSTRUCTIONTYPE_HIGH_RYW 14 -#define VERTEX_OBSTRUCTIONTYPE_LOW_RYW 6 -#define VERTEX_OBSTRUCTIONTYPE_UNKNOWN 0 - -#define VERTEX_OBSTRUCTIONTYPE_MARKED 2 -#define VERTEX_OBSTRUCTIONTYPE_UNMARKED 0 - -#define gp_GetVertexObstructionType(theGraph, v) (theGraph->V[v].flags&VERTEX_OBSTRUCTIONTYPE_MASK) -#define gp_ClearVertexObstructionType(theGraph, v) (theGraph->V[v].flags &= ~VERTEX_OBSTRUCTIONTYPE_MASK) -#define gp_SetVertexObstructionType(theGraph, v, type) (theGraph->V[v].flags |= type) -#define gp_ResetVertexObstructionType(theGraph, v, type) \ - (theGraph->V[v].flags = (theGraph->V[v].flags & ~VERTEX_OBSTRUCTIONTYPE_MASK) | type) - -#define gp_CopyVertexRec(dstGraph, vdst, srcGraph, vsrc) (dstGraph->V[vdst] = srcGraph->V[vsrc]) - -#define gp_SwapVertexRec(dstGraph, vdst, srcGraph, vsrc) \ - { \ - vertexRec tempV = dstGraph->V[vdst]; \ - dstGraph->V[vdst] = srcGraph->V[vsrc]; \ - srcGraph->V[vsrc] = tempV; \ - } - -/******************************************************************** - This structure defines a pair of links used by each vertex and virtual vertex - to create "short circuit" paths that eliminate unimportant vertices from - the external face, enabling more efficient traversal of the external face. - - It is also possible to embed the "short circuit" edges, but this approach - creates a better separation of concerns, imparts greater clarity, and - removes exceptionalities for handling additional fake "short circuit" edges. - - vertex[2]: The two adjacent vertices along the external face, possibly - short-circuiting paths of inactive vertices. -*/ - -typedef struct -{ - int vertex[2]; -} extFaceLinkRec; - -typedef extFaceLinkRec * extFaceLinkRecP; - -#define gp_GetExtFaceVertex(theGraph, v, link) (theGraph->extFace[v].vertex[link]) -#define gp_SetExtFaceVertex(theGraph, v, link, theVertex) (theGraph->extFace[v].vertex[link] = theVertex) - -/******************************************************************** - Vertex Info Structure Definition. - - This structure equips the primary (non-virtual) vertices with additional - information needed for lowpoint and planarity-related algorithms. - - parent: The DFI of the DFS tree parent of this vertex - leastAncestor: min(DFI of neighbors connected by backedge) - lowpoint: min(leastAncestor, min(lowpoint of DFS Children)) - - visitedInfo: enables algorithms to manage vertex visitation with more than - just a flag. For example, the planarity test flags visitation - as a step number that implicitly resets on each step, whereas - part of the planar drawing method signifies a first visitation - by storing the index of the first edge used to reach a vertex - pertinentEdge: Used by the planarity method; during Walkup, each vertex - that is directly adjacent via a back edge to the vertex v - currently being embedded will have the forward edge's index - stored in this field. During Walkdown, each vertex for which - this field is set will cause a back edge to be embedded. - Implicitly resets at each vertex step of the planarity method - pertinentRootsList: used by Walkup to store a list of child bicomp roots of - a vertex descendant of the current vertex that are pertinent - and must be merged by the Walkdown in order to embed the cycle - edges of the current vertex. Future pertinent child bicomp roots - are placed at the end of the list to ensure bicomps that are - only pertinent are processed first. - futurePertinentChild: indicates a DFS child with a lowpoint less than the - current vertex v. This member is initialized to the start of - the sortedDFSChildList and is advanced in a relaxed manner as - needed until one with a lowpoint less than v is found or until - there are no more children. - sortedDFSChildList: at the start of embedding, the list of DFS children of - this vertex is calculated in ascending order by DFI (sorted in - linear time). The list is used during Walkdown processing of - a vertex to process all of its children. It is also used in - future pertinence management when processing the ancestors of - the vertex. When a child C is merged into the same bicomp as - the vertex, it is removed from the list. - fwdArcList: at the start of embedding, the "back" edges from a vertex to - its DFS *descendants* (i.e. the forward arcs of the back edges) - are separated from the main adjacency list and placed in a - circular list until they are embedded. The list is sorted in - ascending DFI order of the descendants (in linear time). - This member indicates a node in that list. -*/ - -typedef struct -{ - int parent, leastAncestor, lowpoint; - - int visitedInfo; - - int pertinentEdge, - pertinentRoots, - futurePertinentChild, - sortedDFSChildList, - fwdArcList; -} vertexInfo; - -typedef vertexInfo * vertexInfoP; - -#define gp_GetVertexVisitedInfo(theGraph, v) (theGraph->VI[v].visitedInfo) -#define gp_SetVertexVisitedInfo(theGraph, v, theVisitedInfo) (theGraph->VI[v].visitedInfo = theVisitedInfo) - -#define gp_GetVertexParent(theGraph, v) (theGraph->VI[v].parent) -#define gp_SetVertexParent(theGraph, v, theParent) (theGraph->VI[v].parent = theParent) - -#define gp_GetVertexLeastAncestor(theGraph, v) (theGraph->VI[v].leastAncestor) -#define gp_SetVertexLeastAncestor(theGraph, v, theLeastAncestor) (theGraph->VI[v].leastAncestor = theLeastAncestor) - -#define gp_GetVertexLowpoint(theGraph, v) (theGraph->VI[v].lowpoint) -#define gp_SetVertexLowpoint(theGraph, v, theLowpoint) (theGraph->VI[v].lowpoint = theLowpoint) - -#define gp_GetVertexPertinentEdge(theGraph, v) (theGraph->VI[v].pertinentEdge) -#define gp_SetVertexPertinentEdge(theGraph, v, e) (theGraph->VI[v].pertinentEdge = e) - -#define gp_GetVertexPertinentRootsList(theGraph, v) (theGraph->VI[v].pertinentRoots) -#define gp_SetVertexPertinentRootsList(theGraph, v, pertinentRootsHead) (theGraph->VI[v].pertinentRoots = pertinentRootsHead) - -#define gp_GetVertexFirstPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, theGraph->VI[v].pertinentRoots) -#define gp_GetVertexFirstPertinentRootChild(theGraph, v) (theGraph->VI[v].pertinentRoots) -#define gp_GetVertexLastPertinentRoot(theGraph, v) gp_GetRootFromDFSChild(theGraph, LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL)) -#define gp_GetVertexLastPertinentRootChild(theGraph, v) LCGetPrev(theGraph->BicompRootLists, theGraph->VI[v].pertinentRoots, NIL) - -#define gp_DeleteVertexPertinentRoot(theGraph, v, R) \ - gp_SetVertexPertinentRootsList(theGraph, v, \ - LCDelete(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) - -#define gp_PrependVertexPertinentRoot(theGraph, v, R) \ - gp_SetVertexPertinentRootsList(theGraph, v, \ - LCPrepend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) - -#define gp_AppendVertexPertinentRoot(theGraph, v, R) \ - gp_SetVertexPertinentRootsList(theGraph, v, \ - LCAppend(theGraph->BicompRootLists, gp_GetVertexPertinentRootsList(theGraph, v), gp_GetDFSChildFromRoot(theGraph, R))) - -#define gp_GetVertexFuturePertinentChild(theGraph, v) (theGraph->VI[v].futurePertinentChild) -#define gp_SetVertexFuturePertinentChild(theGraph, v, theFuturePertinentChild) (theGraph->VI[v].futurePertinentChild = theFuturePertinentChild) - -// Used to advance futurePertinentChild of w to the next separated DFS child with a lowpoint less than v -// Once futurePertinentChild advances past a child, no future planarity operation could make that child -// relevant to future pertinence -#define gp_UpdateVertexFuturePertinentChild(theGraph, w, v) \ - while (gp_IsVertex(theGraph->VI[w].futurePertinentChild)) \ - { \ - /* Skip children that 1) aren't future pertinent, 2) have been merged into the bicomp with w */ \ - if (gp_GetVertexLowpoint(theGraph, theGraph->VI[w].futurePertinentChild) >= v || \ - gp_IsNotSeparatedDFSChild(theGraph, theGraph->VI[w].futurePertinentChild)) \ - { \ - theGraph->VI[w].futurePertinentChild = \ - gp_GetVertexNextDFSChild(theGraph, w, gp_GetVertexFuturePertinentChild(theGraph, w)); \ - } \ - else break; \ - } - -#define gp_GetVertexSortedDFSChildList(theGraph, v) (theGraph->VI[v].sortedDFSChildList) -#define gp_SetVertexSortedDFSChildList(theGraph, v, theSortedDFSChildList) (theGraph->VI[v].sortedDFSChildList = theSortedDFSChildList) - -#define gp_GetVertexNextDFSChild(theGraph, v, c) LCGetNext(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c) - -#define gp_AppendDFSChild(theGraph, v, c) \ - LCAppend(theGraph->sortedDFSChildLists, gp_GetVertexSortedDFSChildList(theGraph, v), c) - -#define gp_GetVertexFwdArcList(theGraph, v) (theGraph->VI[v].fwdArcList) -#define gp_SetVertexFwdArcList(theGraph, v, theFwdArcList) (theGraph->VI[v].fwdArcList = theFwdArcList) - -#define gp_CopyVertexInfo(dstGraph, dstI, srcGraph, srcI) (dstGraph->VI[dstI] = srcGraph->VI[srcI]) - -#define gp_SwapVertexInfo(dstGraph, dstPos, srcGraph, srcPos) \ - { \ - vertexInfo tempVI = dstGraph->VI[dstPos]; \ - dstGraph->VI[dstPos] = srcGraph->VI[srcPos]; \ - srcGraph->VI[srcPos] = tempVI; \ - } - -/******************************************************************** - Variables needed in embedding by Kuratowski subgraph isolator: - minorType: the type of planarity obstruction found. - v: the current vertex being processed - r: the root of the bicomp on which the Walkdown failed - x,y: stopping vertices on bicomp rooted by r - w: pertinent vertex on ext. face path below x and y - px, py: attachment points of x-y path, - z: Unused except in minors D and E (not needed in A, B, C). - - ux,dx: endpoints of unembedded edge that helps connext x with - ancestor of v - uy,dy: endpoints of unembedded edge that helps connext y with - ancestor of v - dw: descendant endpoint in unembedded edge to v - uz,dz: endpoints of unembedded edge that helps connext z with - ancestor of v (for minors B and E, not A, C, D). -*/ - -typedef struct -{ - int minorType; - int v, r, x, y, w, px, py, z; - int ux, dx, uy, dy, dw, uz, dz; -} isolatorContext; - -typedef isolatorContext * isolatorContextP; - -#define MINORTYPE_A 1 -#define MINORTYPE_B 2 -#define MINORTYPE_C 4 -#define MINORTYPE_D 8 -#define MINORTYPE_E 16 -#define MINORTYPE_E1 32 -#define MINORTYPE_E2 64 -#define MINORTYPE_E3 128 -#define MINORTYPE_E4 256 - -#define MINORTYPE_E5 512 -#define MINORTYPE_E6 1024 -#define MINORTYPE_E7 2048 - -/******************************************************************** - Graph structure definition - V : Array of vertex records (allocated size N + NV) - VI: Array of additional vertexInfo structures (allocated size N) - N : Number of primary vertices (the "order" of the graph) - NV: Number of virtual vertices (currently always equal to N) - - E : Array of edge records (edge records come in pairs and represent half edges, or arcs) - M: Number of edges (the "size" of the graph) - arcCapacity: the maximum number of edge records allowed in E (the size of E) - edgeHoles: free locations in E where edges have been deleted - - theStack: Used by various graph routines needing a stack - internalFlags: Additional state information about the graph - embedFlags: controls type of embedding (e.g. planar) - - IC: contains additional useful variables for Kuratowski subgraph isolation. - BicompRootLists: storage space for pertinent bicomp root lists that develop - during embedding - sortedDFSChildLists: storage for the sorted DFS child lists of each vertex - extFace: Array of (N + NV) external face short circuit records - - extensions: a list of extension data structures - functions: a table of function pointers that can be overloaded to provide - extension behaviors to the graph -*/ - -typedef struct -{ - vertexRecP V; - vertexInfoP VI; - int N, NV; - - edgeRecP E; - int M, arcCapacity; - stackP edgeHoles; - - stackP theStack; - int internalFlags, embedFlags; - - isolatorContext IC; - listCollectionP BicompRootLists, sortedDFSChildLists; - extFaceLinkRecP extFace; - - graphExtensionP extensions; - graphFunctionTable functions; - -} baseGraphStructure; - -typedef baseGraphStructure * graphP; - -/* Flags for graph: - FLAGS_DFSNUMBERED is set if DFSNumber() has succeeded for the graph - FLAGS_SORTEDBYDFI records whether the graph is in original vertex - order or sorted by depth first index. Successive calls to - SortVertices() toggle this bit. - FLAGS_OBSTRUCTIONFOUND is set by gp_Embed() if an embedding obstruction - was isolated in the graph returned. It is cleared by gp_Embed() - if an obstruction was not found. The flag is used by - gp_TestEmbedResultIntegrity() to decide what integrity tests to run. - FLAGS_ZEROBASEDIO is typically set by gp_Read() to indicate that the - adjacency list representation began with index 0. -*/ - -#define FLAGS_DFSNUMBERED 1 -#define FLAGS_SORTEDBYDFI 2 -#define FLAGS_OBSTRUCTIONFOUND 4 -#define FLAGS_ZEROBASEDIO 8 - -/******************************************************************** - More link structure accessors/manipulators - ********************************************************************/ - -// Definitions that enable getting the next or previous arc -// as if the adjacency list were circular, i.e. that the -// first arc and last arc were linked -#define gp_GetNextArcCircular(theGraph, e) \ - (gp_IsArc(gp_GetNextArc(theGraph, e)) ? \ - gp_GetNextArc(theGraph, e) : \ - gp_GetFirstArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor)) - -#define gp_GetPrevArcCircular(theGraph, e) \ - (gp_IsArc(gp_GetPrevArc(theGraph, e)) ? \ - gp_GetPrevArc(theGraph, e) : \ - gp_GetLastArc(theGraph, theGraph->E[gp_GetTwinArc(theGraph, e)].neighbor)) - -// Definitions that make the cross-link binding between a vertex and an arc -// The old first or last arc should be bound to this arc by separate calls, -// e.g. see gp_AttachFirstArc() and gp_AttachLastArc() -#define gp_BindFirstArc(theGraph, v, arc) \ - { \ - gp_SetPrevArc(theGraph, arc, NIL); \ - gp_SetFirstArc(theGraph, v, arc); \ - } - -#define gp_BindLastArc(theGraph, v, arc) \ - { \ - gp_SetNextArc(theGraph, arc, NIL); \ - gp_SetLastArc(theGraph, v, arc); \ - } - -// Attaches an arc between the current binding between a vertex and its first arc -#define gp_AttachFirstArc(theGraph, v, arc) \ - { \ - if (gp_IsArc(gp_GetFirstArc(theGraph, v))) \ - { \ - gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \ - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \ - } \ - else gp_BindLastArc(theGraph, v, arc); \ - gp_BindFirstArc(theGraph, v, arc); \ - } - -// Attaches an arc between the current binding betwen a vertex and its last arc -#define gp_AttachLastArc(theGraph, v, arc) \ - { \ - if (gp_IsArc(gp_GetLastArc(theGraph, v))) \ - { \ - gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \ - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \ - } \ - else gp_BindFirstArc(theGraph, v, arc); \ - gp_BindLastArc(theGraph, v, arc); \ - } - -// Moves an arc that is in the adjacency list of v to the start of the adjacency list -#define gp_MoveArcToFirst(theGraph, v, arc) \ - if (arc != gp_GetFirstArc(theGraph, v)) \ - { \ - /* If the arc is last in the adjacency list of uparent, - then we delete it by adjacency list end management */ \ - if (arc == gp_GetLastArc(theGraph, v)) \ - { \ - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), NIL); \ - gp_SetLastArc(theGraph, v, gp_GetPrevArc(theGraph, arc)); \ - } \ - /* Otherwise, we delete the arc from the middle of the list */ \ - else \ - { \ - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \ - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \ - } \ -\ - /* Now add arc e as the new first arc of uparent. - Note that the adjacency list is non-empty at this time */ \ - gp_SetNextArc(theGraph, arc, gp_GetFirstArc(theGraph, v)); \ - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), arc); \ - gp_BindFirstArc(theGraph, v, arc); \ - } - -// Moves an arc that is in the adjacency list of v to the end of the adjacency list -#define gp_MoveArcToLast(theGraph, v, arc) \ - if (arc != gp_GetLastArc(theGraph, v)) \ - { \ - /* If the arc is first in the adjacency list of vertex v, - then we delete it by adjacency list end management */ \ - if (arc == gp_GetFirstArc(theGraph, v)) \ - { \ - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), NIL); \ - gp_SetFirstArc(theGraph, v, gp_GetNextArc(theGraph, arc)); \ - } \ - /* Otherwise, we delete the arc from the middle of the list */ \ - else \ - { \ - gp_SetNextArc(theGraph, gp_GetPrevArc(theGraph, arc), gp_GetNextArc(theGraph, arc)); \ - gp_SetPrevArc(theGraph, gp_GetNextArc(theGraph, arc), gp_GetPrevArc(theGraph, arc)); \ - } \ -\ - /* Now add the arc as the new last arc of v. - Note that the adjacency list is non-empty at this time */ \ - gp_SetPrevArc(theGraph, arc, gp_GetLastArc(theGraph, v)); \ - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), arc); \ - gp_BindLastArc(theGraph, v, arc); \ - } - -// Methods for attaching an arc into the adjacency list or detaching an arc from it. -// The terms AddArc, InsertArc and DeleteArc are not used because the arcs are not -// inserted or added to or deleted from storage (only whole edges are inserted or deleted) -void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc); -void gp_DetachArc(graphP theGraph, int arc); - -/******************************************************************** - PERTINENT() - A vertex is pertinent in a partially processed graph if there is an - unprocessed back edge between the vertex v whose edges are currently - being processed and either the vertex or a DFS descendant D of the - vertex not in the same bicomp as the vertex. - - The vertex is either directly adjacent to v by an unembedded back edge - or there is an unembedded back edge (v, D) and the vertex is a cut - vertex in the partially processed graph along the DFS tree path from - D to v. - - Pertinence is a dynamic property that can change for a vertex after - each edge addition. In other words, a vertex can become non-pertinent - during step v as more back edges to v are embedded. - ********************************************************************/ - -#define PERTINENT(theGraph, theVertex) \ - (gp_IsArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) || \ - gp_IsVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex))) - -#define NOTPERTINENT(theGraph, theVertex) \ - (gp_IsNotArc(gp_GetVertexPertinentEdge(theGraph, theVertex)) && \ - gp_IsNotVertex(gp_GetVertexPertinentRootsList(theGraph, theVertex))) - -/******************************************************************** - FUTUREPERTINENT() - A vertex is future-pertinent in a partially processed graph if - there is an unprocessed back edge between a DFS ancestor A of the - vertex v whose edges are currently being processed and either - theVertex or a DFS descendant D of theVertex not in the same bicomp - as theVertex. - - Either theVertex is directly adjacent to A by an unembedded back edge - or there is an unembedded back edge (A, D) and theVertex is a cut - vertex in the partially processed graph along the DFS tree path from - D to A. - - If no more edges are added to the partially processed graph prior to - processing the edges of A, then the vertex would be pertinent. - The addition of edges to the partially processed graph can alter - both the pertinence and future pertinence of a vertex. For example, - if the vertex is pertinent due to an unprocessed back edge (v, D1) and - future pertinent due to an unprocessed back edge (A, D2), then the - vertex may lose both its pertinence and future pertinence when edge - (v, D1) is added if D2 is in the same subtree as D1. - - Generally, pertinence and future pertinence are dynamic properties - that can change for a vertex after each edge addition. - - Note that gp_UpdateVertexFuturePertinentChild() must be called before - this macro. Since it is a statement and not a void expression, the - desired commented out version does not compile (except with special - compiler extensions not assumed by this code). - ********************************************************************/ - -#define FUTUREPERTINENT(theGraph, theVertex, v) \ - ( theGraph->VI[theVertex].leastAncestor < v || \ - (gp_IsVertex(theGraph->VI[theVertex].futurePertinentChild) && \ - theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) ) - -#define NOTFUTUREPERTINENT(theGraph, theVertex, v) \ - ( theGraph->VI[theVertex].leastAncestor >= v && \ - (gp_IsNotVertex(theGraph->VI[theVertex].futurePertinentChild) || \ - theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint >= v) ) - -// This is the definition that would be preferrable if a while loop could be a void expression -//#define FUTUREPERTINENT(theGraph, theVertex, v) -// ( theGraph->VI[theVertex].leastAncestor < v || -// ((gp_UpdateVertexFuturePertinentChild(theGraph, theVertex, v), -// gp_IsArc(theGraph->VI[theVertex].futurePertinentChild)) && -// theGraph->VI[theGraph->VI[theVertex].futurePertinentChild].lowpoint < v) ) - -/******************************************************************** - INACTIVE() - For planarity algorithms, a vertex is inactive if it is neither pertinent - nor future pertinent. - ********************************************************************/ - -#define INACTIVE(theGraph, theVertex, v) \ - ( NOTPERTINENT(theGraph, theVertex) && \ - NOTFUTUREPERTINENT(theGraph, theVertex, v)) - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphTests.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphTests.c deleted file mode 100644 index c8fe95d3c..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphTests.c +++ /dev/null @@ -1,976 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif - -#define GRAPHTEST_C - -#include "graph.h" -#include "stack.h" - -extern void _ClearVertexVisitedFlags(graphP theGraph, int); - -/* Private function declarations */ - -int _TestPath(graphP theGraph, int U, int V); -int _TryPath(graphP theGraph, int e, int V); -void _MarkPath(graphP theGraph, int e); -int _TestSubgraph(graphP theSubgraph, graphP theGraph); - -int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -int _CheckEmbeddingFacialIntegrity(graphP theGraph); -int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph); - -int _CheckKuratowskiSubgraphIntegrity(graphP theGraph); -int _CheckOuterplanarObstructionIntegrity(graphP theGraph); - -int _CheckAllVerticesOnExternalFace(graphP theGraph); -void _MarkExternalFaceVertices(graphP theGraph, int startVertex); - -/******************************************************************** - gp_TestEmbedResultIntegrity() - - This function tests the integrity of the graph result returned - from gp_Embed(). - - The caller of gp_Embed() does not have to save the original graph - because, for efficiency, gp_Embed() operates on the input graph. - However, to test the integrity of the result relative to the input, - a copy of the input graph is required. - - Modules that extend/alter the behavior of gp_Embed() beyond the - core planarity embedder and planarity obstruction isolator should - also provide overriding integrity test routines appropriate to the - extension algorithm. - - The main method first calls gp_SortVertices on theGraph, if the - origGraph is not in DFI order (the common case). Therefore, - extension integrity tests can count on a consistent numbering - between theGraph and the origGraph, either DFI order or pre-DFS - order if that is the state of the origGraph. - - After all tests, the main method ensures theGraph is restored to - DFI order by invoking gp_SortVertices if needed, thus ensuring - that theGraph has the documented post-condition of gp_Embed(). - - For an embedResult of OK, fpCheckEmbeddingIntegrity is invoked. - The core planarity implementation does a face walk of all faces - of the embedding. It ensures that all edges were used in the face - walk and that the right number of faces exist for the number of - vertices and edges. Also, we ensure that all adjacencies expressed - in the original graph still exist in the result graph. - - For an embedResult of NONEMBEDDABLE, fpCheckObstructionIntegrity - is invoked. The core planarity algorithm checks that the result - graph is homeomorphic to K5 or K3,3 and that it is in fact a - subgraph of the input graph. Other algorithms use overloads to - make appropriate checks. - - Returns NOTOK on integrity check failure or embedResult of NOTOK - OK for successful integrity check of OK embedResult - NONEMBEDDABLE for successful integrity check of an - embedResult of NONEMBEDDABLE - ********************************************************************/ - -int gp_TestEmbedResultIntegrity(graphP theGraph, graphP origGraph, int embedResult) -{ -int RetVal = embedResult; - - if (theGraph == NULL || origGraph == NULL) - return NOTOK; - - if (embedResult == OK) - { - RetVal = theGraph->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph); - } - else if (embedResult == NONEMBEDDABLE) - { - RetVal = theGraph->functions.fpCheckObstructionIntegrity(theGraph, origGraph); - } - - if (RetVal == OK) - RetVal = embedResult; - - return RetVal; -} - -/******************************************************************** - _CheckEmbeddingIntegrity() - - The core planarity implementation does a face walk of all faces - of the embedding. It ensures that all edges were used in the face - walk and that the right number of faces exist for the number of - vertices and edges. Also, we ensure that all adjacencies expressed - in the original graph still exist in the result graph, accounting - for the fact that the result graph is sorted by DFI, but the input - may or may not be sorted by DFI. - - returns OK if all integrity tests passed, NOTOK otherwise - ********************************************************************/ - -int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph) -{ - if (theGraph == NULL || origGraph == NULL) - return NOTOK; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - return NOTOK; - - if (_TestSubgraph(origGraph, theGraph) != TRUE) - return NOTOK; - - if (_CheckEmbeddingFacialIntegrity(theGraph) != OK) - return NOTOK; - - if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR) - { - if (_CheckAllVerticesOnExternalFace(theGraph) != OK) - return NOTOK; - } - - return OK; -} - -/******************************************************************** - _CheckEmbeddingFacialIntegrity() - - This function traverses all faces of a graph structure containing - the planar embedding that results from gp_Embed(). The algorithm - begins by placing all of the graph's arcs onto a stack and marking - all of them as unvisited. For each arc popped, if it is visited, - it is immediately discarded and the next arc is popped. Popping an - unvisited arc e begins a face traversal. We move to the true twin - arc of e, and obtain its successor arc. This amounts to always - going clockwise or counterclockwise (depending on how the graph is - drawn on the plane, or alternately whether one is above or below - the plane). This traversal continues until we make it back to the - original arc e. Each arc along the way is marked as visited. Further, - if the successor arc has been visited, then there is an error since - an arc can only appear in one face (the twin arc appears in a separate - face, which is traversed in the opposing direction). - If this algorithm succeeds without double visiting any arcs, and it - produces the correct face count according to Euler's formula, then - the embedding has all vertices oriented the same way. - NOTE: In disconnected graphs, the face reader counts the external - face of each connected component. So, we adjust the face - count by subtracting one for each component, then we add one - to count the external face shared by all components. - ********************************************************************/ - -int _CheckEmbeddingFacialIntegrity(graphP theGraph) -{ -stackP theStack = theGraph->theStack; -int EsizeOccupied, v, e, eTwin, eStart, eNext, NumFaces, connectedComponents; - - if (theGraph == NULL) - return NOTOK; - -/* The stack need only contain 2M entries, one for each edge record. With - max M at 3N, this amounts to 6N integers of space. The embedding - structure already contains this stack, so we just make sure it - starts out empty. */ - - sp_ClearStack(theStack); - -/* Push all arcs and set them to unvisited */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2) - { - // Except skip edge holes - if (gp_EdgeInUse(theGraph, e)) - { - sp_Push(theStack, e); - gp_ClearEdgeVisited(theGraph, e); - eTwin = gp_GetTwinArc(theGraph, e); - sp_Push(theStack, eTwin); - gp_ClearEdgeVisited(theGraph, eTwin); - } - } - - // There are M edges, so we better have pushed 2M arcs just now - // i.e. testing that the continue above skipped only edge holes - if (sp_GetCurrentSize(theStack) != 2*theGraph->M) - return NOTOK; - - -/* Read faces until every arc is used */ - - NumFaces = 0; - while (sp_NonEmpty(theStack)) - { - /* Get an arc; if it has already been used by a face, then - don't use it to traverse a new face */ - sp_Pop(theStack, eStart); - if (gp_GetEdgeVisited(theGraph, eStart)) continue; - - e = eStart; - do { - eNext = gp_GetNextArcCircular(theGraph, gp_GetTwinArc(theGraph, e)); - if (gp_GetEdgeVisited(theGraph, eNext)) - return NOTOK; - gp_SetEdgeVisited(theGraph, eNext); - e = eNext; - } while (e != eStart); - NumFaces++; - } - -/* Count the external face once rather than once per connected component; - each connected component is detected by the fact that it has no - DFS parent, except in the case of isolated vertices, no face was counted - so we do not subtract one. */ - - connectedComponents = 0; - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - if (gp_IsDFSTreeRoot(theGraph, v)) - { - if (gp_GetVertexDegree(theGraph, v) > 0) - NumFaces--; - connectedComponents++; - } - } - - NumFaces++; - -/* Test number of faces using the extended Euler's formula. - For connected components, Euler's formula is f=m-n+2, but - for disconnected graphs it is extended to f=m-n+1+c where - c is the number of connected components.*/ - - return NumFaces == theGraph->M - theGraph->N + 1 + connectedComponents - ? OK : NOTOK; -} - -/******************************************************************** - _CheckAllVerticesOnExternalFace() - - Determines whether or not any vertices have been embedded within - the bounding cycle of the external face. - The input graph may be disconnected, so this routine walks the - external face starting at each vertex with no DFSParent. - - return OK if all vertices visited on external face walks, NOTOK otherwise - ********************************************************************/ - -int _CheckAllVerticesOnExternalFace(graphP theGraph) -{ - int v; - - // Mark all vertices unvisited - _ClearVertexVisitedFlags(theGraph, FALSE); - - // For each connected component, walk its external face and - // mark the vertices as visited - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - if (gp_IsDFSTreeRoot(theGraph, v)) - _MarkExternalFaceVertices(theGraph, v); - } - - // If any vertex is unvisited, then the embedding is not an outerplanar - // embedding, so we return NOTOK - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - if (!gp_GetVertexVisited(theGraph, v)) - return NOTOK; - - // All vertices were found on external faces of the connected components - // so the embedding is an outerplanar embedding and we return OK - return OK; -} - -/******************************************************************** - _MarkExternalFaceVertices() - - Walks the external face of the connected component containing the - start vertex, and marks the visited flag of all vertices found. - The start vertex is assumed to be on the external face. - This method assumed the embedding integrity has already been - verified to be correct. - This method correctly handles components that have cut vertices, - i.e. it does not assume that the outer face is a simple cycle; - it only assumes that all vertices are reachable by walking a - single face that starts with startVertex. - ********************************************************************/ - -void _MarkExternalFaceVertices(graphP theGraph, int startVertex) -{ - int nextVertex = startVertex; - int e = gp_GetFirstArc(theGraph, nextVertex); - int eTwin; - - // Handle the case of an isolated vertex - if (gp_IsNotArc(e)) - { - gp_SetVertexVisited(theGraph, startVertex); - return; - } - - // Process a non-trivial connected component - do { - gp_SetVertexVisited(theGraph, nextVertex); - - // The arc out of the vertex just visited points to the next vertex - nextVertex = gp_GetNeighbor(theGraph, e); - - // Arc used to enter the next vertex is needed so we can get the - // next edge in rotation order. - // Note: for bicomps, first and last arcs of all external face vertices - // indicate the edges that hold them to the external face - // But _JoinBicomps() has already occurred, so cut vertices - // will have external face edges other than the first and last arcs - // Hence we need this more sophisticated traversal method - eTwin = gp_GetTwinArc(theGraph, e); - - // Now we get the next arc in rotation order as the new arc out to the - // vertex after nextVertex. This sets us up for the next iteration. - // Note: We cannot simply follow the chain of nextVertex first arcs - // as we started out doing at the top of this method. This is - // because we are no longer dealing with bicomps only. - // Since _JoinBicomps() has already been invoked, there may now - // be cut vertices on the external face whose adjacency lists - // contain external face arcs in positions other than the first and - // and last arcs. We will visit those vertices multiple times, - // which is OK (just that we have to explain why we're calculating - // jout in this way). - e = gp_GetNextArcCircular(theGraph, eTwin); - - // Now things get really interesting. The DFS root (startVertex) may - // itself be a cut vertex to which multiple bicomps have been joined. - // So we cannot simply stop when the external face walk gets back to - // startVertex. We must actually get back to startVertex using its - // last arc. This ensures that we've looped down into all the DFS - // subtrees rooted at startVertex and walked their external faces. - - // Since we started the whole external face walk with the first arc - // of startVertex, we need to proceed until we reenter startVertex - // using its last arc. - - } while (eTwin != gp_GetLastArc(theGraph, startVertex)); -} - - -/******************************************************************** - _CheckObstructionIntegrity() - - Returns OK if theGraph is a subgraph of origGraph and it contains - an allowed homeomorph, and NOTOK otherwise. - - For core planarity, the allowed homeomorphs are K_5 or K_{3,3} - - Extension modules may overload this method to implement different - tests. For example, K_{3,3} search allows only K_{3,3} and - outerplanarity allows only K_4 or K_{2,3}. - ********************************************************************/ - -int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph) -{ - if (theGraph == NULL || origGraph == NULL) - return NOTOK; - - if (_TestSubgraph(theGraph, origGraph) != TRUE) - { - return NOTOK; - } - - if (theGraph->embedFlags == EMBEDFLAGS_PLANAR) - return _CheckKuratowskiSubgraphIntegrity(theGraph); - - else if (theGraph->embedFlags == EMBEDFLAGS_OUTERPLANAR) - return _CheckOuterplanarObstructionIntegrity(theGraph); - - return NOTOK; -} - -/******************************************************************** - _getImageVertices() - - Count the number of vertices of each degree and find the locations of - the image vertices (also sometimes called the corners of the obstruction). - An image vertex is a vertex of degree three or higher because degree - 2 vertices are generally internal to the paths between the image - vertices. - - The notable exception is K_{2,3}, an obstruction to outerplanarity. - This routine does not know the obstruction it is looking for, so the - caller must decide whether there are any degree 2 vertices that should - be added to imageVerts. - - Return NOTOK if any vertex of degree 1 or higher than the max is found - NOTOK if more than the max number of image vertices is found. - Return OK otherwise. - ********************************************************************/ - -int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, - int *imageVerts, int maxNumImageVerts) -{ -int K, v, imageVertPos, degree; - - for (degree = 0; degree <= maxDegree; degree++) - degrees[degree] = 0; - - for (K = 0; K < maxNumImageVerts; K++) - imageVerts[K] = NIL; - - imageVertPos = 0; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - degree = gp_GetVertexDegree(theGraph, v); - if (degree == 1) - return NOTOK; - if (degree > maxDegree) - return NOTOK; - - degrees[degree]++; - - if (imageVertPos < maxNumImageVerts && degree > 2) - imageVerts[imageVertPos++] = v; - else if (degree > 2) - return NOTOK; - } - - return OK; -} - -/******************************************************************** - _TestForCompleteGraphObstruction() - - theGraph - the graph to test - numVerts - the number of image vertices (corners) of the complete - graph homeomorph being tested for (e.g. 5 for a K5) - degrees - array of counts of the number of vertices of each degree - given by the array index. Only valid up to numVerts-1 - imageVerts - the vertices of degree numVerts-1 - - This routine tests whether theGraph is a K_{numVerts} homeomorph for - numVerts >= 4. - - returns FALSE if numVerts < 4, - if theGraph has other than numVerts image vertices - if theGraph contains other than degree 2 vertices plus - the image vertices - if any pair of image vertices lacks a connecting path - if any degree two vertices are not in the connecting paths - TRUE otherwise - ********************************************************************/ - -int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, - int *degrees, int *imageVerts) -{ - int v, w; - - // We need to make sure we have numVerts vertices of degree numVerts-1 - // For example, if numVerts==5, then we're looking for a K5, so we - // need to have degrees[4] == 5 (5 vertices of degree 4) - if (degrees[numVerts-1] != numVerts) - return FALSE; - - // All vertices need to be degree 0, degree 2 or degree numVerts-1 - if (degrees[0]+degrees[2]+degrees[numVerts-1] != theGraph->N) - return FALSE; - - // We clear all the vertex visited flags - _ClearVertexVisitedFlags(theGraph, FALSE); - - // For each pair of image vertices, we test that there is a path - // between the two vertices. If so, the visited flags of the - // internal vertices along the path are marked - // - for (v = 0; v < numVerts; v++) - for (w = 0; w < numVerts; w++) - if (v != w) - if (_TestPath(theGraph, imageVerts[v], - imageVerts[w]) != TRUE) - return FALSE; - - // The visited flags should have marked only degree two vertices, - // so for every marked vertex, we subtract one from the count of - // the degree two vertices. - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - if (gp_GetVertexVisited(theGraph, v)) - degrees[2]--; - - /* If every degree 2 vertex is used in a path between image - vertices, then there are no extra pieces of the graph - in theGraph. Specifically, the prior tests identify - a K_5 and ensure that nothing else could exist in the - graph except extra degree 2 vertices, which must be - joined in a cycle so that all are degree 2. */ - - return degrees[2] == 0 ? TRUE : FALSE; -} - -/******************************************************************** - _TestForK33GraphObstruction() - - theGraph - the graph to test - degrees - array of counts of the number of vertices of each degree - given by the array index. Only valid up to numVerts-1 - imageVerts - the degree 3 vertices of the K3,3 homeomorph - - This routine tests whether theGraph is a K_{3,3} homeomorph. - - returns TRUE if so, FALSE if not - ********************************************************************/ - -int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) -{ -int v, K, imageVertPos, temp, success; - - if (degrees[4] != 0) - return FALSE; - - if (degrees[3] != 6) - return FALSE; - - /* Partition the six image vertices into two sets of 3 - (or report failure) */ - - for (imageVertPos = 3; imageVertPos < 6; imageVertPos++) - { - K = 0; - success = FALSE; - do { - if (_TestPath(theGraph, imageVerts[imageVertPos], imageVerts[0]) == TRUE) - { - success = TRUE; - break; - } - - K++; - temp = imageVerts[K]; - imageVerts[K] = imageVerts[imageVertPos]; - imageVerts[imageVertPos] = temp; - } while (K < 3); - - if (!success) - return FALSE; - } - - /* Now test the paths between each of the first three vertices and - each of the last three vertices */ - - _ClearVertexVisitedFlags(theGraph, FALSE); - - for (imageVertPos=0; imageVertPos<3; imageVertPos++) - for (K=3; K<6; K++) - if (_TestPath(theGraph, imageVerts[imageVertPos], - imageVerts[K]) != TRUE) - return FALSE; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - if (gp_GetVertexVisited(theGraph, v)) - degrees[2]--; - - /* If every degree 2 vertex is used in a path between image - vertices, then there are no extra pieces of the graph - in theGraph. Specifically, the prior tests identify - a K_{3,3} and ensure that nothing else could exist in the - graph except extra degree 2 vertices, which must be - joined in a cycle so that all are degree 2. */ - - return degrees[2] == 0 ? TRUE : FALSE; -} - -/******************************************************************** - _CheckKuratowskiSubgraphIntegrity() - - This function checks whether theGraph received as input contains - either a K_5 or K_{3,3} homeomorph. - - RETURNS: OK if theGraph contains a K_5 or K_{3,3} homeomorph, - NOTOK otherwise - - To be a K_5 homeomorph, there must be exactly 5 vertices of degree 4, - which are called 'image' vertices, and all other vertices must be - either degree 2 or degree 0. Furthermore, each of the image vertices - must be able to reach all of the other image vertices by a single edge - or a path of degree two vertices. - - To be a K_{3,3} homeomorph, there must be exactly 6 vertices of degree 3, - which are called 'image' vertices, and all other vertices must be either - degree 2 or degree 0. Furthermore, the image vertices must be connected - by edges or paths of degree two vertices in the manner suggested by - a K_{3,3}. To test this, we select an image vertex U, and determine - three image vertices X, Y and Z reachable from U by single edges or - paths of degree 2 vertices. Then, we check that the two remaining image - vertices, V and W, can also reach X, Y and Z by single edges or paths of - degree 2 vertices. - - It is not necessary to check that the paths between the image vertices - are distinct since if the paths had a common vertex, then the common - vertex would not be degree 2. - ********************************************************************/ - -int _CheckKuratowskiSubgraphIntegrity(graphP theGraph) -{ -int degrees[5], imageVerts[6]; - - if (_getImageVertices(theGraph, degrees, 4, imageVerts, 6) != OK) - return NOTOK; - - if (_TestForCompleteGraphObstruction(theGraph, 5, degrees, imageVerts) == TRUE) - { - return OK; - } - - if (_TestForK33GraphObstruction(theGraph, degrees, imageVerts) == TRUE) - { - return OK; - } - - return NOTOK; -} - -/******************************************************************** - _TestForK23GraphObstruction() - - theGraph - the graph to test - degrees - array of counts of the number of vertices of each degree - given by the array index. Only valid up to numVerts-1 - imageVerts - the degree 3 vertices of the K2,3 homeomorph - - This routine tests whether theGraph is a K_{2,3} homeomorph. - This routine operates over the results of _getImageVertices() - - returns TRUE if so, FALSE if not - ********************************************************************/ - -int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) -{ -int v, e, imageVertPos; - - // This function operates over the imageVerts results produced by - // getImageVertices, which only finds vertices of degree 3 or higher. - // So, for a K2,3, there must be exactly two degree 3 vertices and - // no degree 4 vertices. - if (degrees[3] != 2) - return FALSE; - - // For K_{2,3}, the three vertices of degree 2 were not - // detected as image vertices because degree 2 vertices - // are indistinguishable from the internal path vertices - // between the image vertices. So, here we acknowledge - // that more image vertices need to be selected. - imageVertPos = 2; - - // Assign the remaining three image vertices to be the - // neighbors of the first degree 3 image vertex. - // Ensure that each is distinct from the second - // degree 3 image vertex. This must be the case because - // the two degree 3 image vertices are in the same partition - // and hence must not be adjacent. - - e = gp_GetFirstArc(theGraph, imageVerts[0]); - while (gp_IsArc(e)) - { - imageVerts[imageVertPos] = gp_GetNeighbor(theGraph, e); - if (imageVerts[imageVertPos] == imageVerts[1]) - return FALSE; - imageVertPos++; - e = gp_GetNextArc(theGraph, e); - } - - /* The paths from imageVerts[0] to each of the new degree 2 - image vertices are the edges we just traversed. - Now test the paths between each of the degree 2 image - vertices and imageVerts[1]. */ - - _ClearVertexVisitedFlags(theGraph, FALSE); - - for (imageVertPos=2; imageVertPos<5; imageVertPos++) - { - if (_TestPath(theGraph, imageVerts[imageVertPos], - imageVerts[1]) != TRUE) - return FALSE; - - gp_SetVertexVisited(theGraph, imageVerts[imageVertPos]); - } - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - if (gp_GetVertexVisited(theGraph, v)) - degrees[2]--; - - /* If every degree 2 vertex is used in a path between the - two degree 3 image vertices, then there are no extra - pieces of the graph in theGraph. Specifically, the - prior tests identify a K_{2,3} and ensure that nothing - else could exist in the graph... except extra degree 2 - vertices joined in a cycle. We return NOTOK in that case. */ - - return degrees[2] == 0 ? TRUE : FALSE; -} - -/******************************************************************** - _CheckOuterplanarObstructionIntegrity() - - This function checks whether theGraph received as input contains - either a K_4 or K_{2,3} homeomorph. - - RETURNS: OK if theGraph contains a K_4 or K_{2,3} homeomorph, - NOTOK otherwise - - To be a K_4 homeomorph, there must be exactly 4 vertices of degree 3, - which are called 'image' vertices, and all other vertices must be - either degree 2 or degree 0. Furthermore, each of the image vertices - must be able to reach all of the other image vertices by a single edge - or a path of degree two vertices. - - To be a K_{2,3} homeomorph, there must be exactly 2 vertices of degree 3. - All other vertices must be degree 2. Furthermore, the two degree 3 - vertices must have three internally disjoint paths connecting them, - and each path must contain at least two edges (i.e. at least one internal - vertex). The two degree 3 vertices are image vertices, and an internal - vertex from each of the three paths contributes the remaining three - image vertices. - - It is not necessary to check that the paths between the degree three - vertices are distinct since if the paths had a common vertex, then the - common vertex would not be degree 2. - ********************************************************************/ - -int _CheckOuterplanarObstructionIntegrity(graphP theGraph) -{ -int degrees[4], imageVerts[5]; - - if (_getImageVertices(theGraph, degrees, 3, imageVerts, 5) != OK) - return NOTOK; - - if (_TestForCompleteGraphObstruction(theGraph, 4, degrees, imageVerts) == TRUE) - { - return OK; - } - - if (_TestForK23GraphObstruction(theGraph, degrees, imageVerts) == TRUE) - { - return OK; - } - -/* We get here only if we failed to recognize an outerplanarity - obstruction, so we return failure */ - - return NOTOK; -} - -/******************************************************************** - _TestPath() - - This function determines whether there exists a path of degree two - vertices between two given vertices. The function marks each - degree two vertex as visited. It returns TRUE if it finds the - path and FALSE otherwise. - ********************************************************************/ - -int _TestPath(graphP theGraph, int U, int V) -{ - int e = gp_GetFirstArc(theGraph, U); - - while (gp_IsArc(e)) - { - if (_TryPath(theGraph, e, V) == OK) - { - _MarkPath(theGraph, e); - return TRUE; - } - - e = gp_GetNextArc(theGraph, e); - } - - return FALSE; - } - -/******************************************************************** - _TryPath() - - This function seeks a given path to a vertex V starting with a - given edge out of a starting vertex U. The path is allowed to - contain zero or more degree two vertices, but we stop as soon as - a vertex of degree higher than two is encountered. - The function returns boolean true if that vertex is V, and - boolean false otherwise. - ********************************************************************/ - -int _TryPath(graphP theGraph, int e, int V) -{ -int eTwin, nextVertex; - - nextVertex = gp_GetNeighbor(theGraph, e); - - // while nextVertex is strictly degree 2 - while (gp_IsArc(gp_GetFirstArc(theGraph, nextVertex)) && - gp_IsArc(gp_GetLastArc(theGraph, nextVertex)) && - gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex)) - { - eTwin = gp_GetTwinArc(theGraph, e); - e = gp_GetFirstArc(theGraph, nextVertex); - if (e == eTwin) - e = gp_GetLastArc(theGraph, nextVertex); - - nextVertex = gp_GetNeighbor(theGraph, e); - } - - return nextVertex == V ? TRUE : FALSE; -} - -/******************************************************************** - _MarkPath() - - This function sets the visitation flag on all degree two vertices - along a path to a vertex V that starts with a given edge out of - a starting vertex U. - ********************************************************************/ - -void _MarkPath(graphP theGraph, int e) -{ -int eTwin, nextVertex; - - nextVertex = gp_GetNeighbor(theGraph, e); - // while nextVertex is strictly degree 2 - while (gp_IsArc(gp_GetFirstArc(theGraph, nextVertex)) && - gp_IsArc(gp_GetLastArc(theGraph, nextVertex)) && - gp_GetNextArc(theGraph, gp_GetFirstArc(theGraph, nextVertex)) == gp_GetLastArc(theGraph, nextVertex)) - { - gp_SetVertexVisited(theGraph, nextVertex); - - eTwin = gp_GetTwinArc(theGraph, e); - e = gp_GetFirstArc(theGraph, nextVertex); - if (e == eTwin) - e = gp_GetLastArc(theGraph, nextVertex); - - nextVertex = gp_GetNeighbor(theGraph, e); - } -} - -/******************************************************************** - _TestSubgraph() - Checks whether theSubgraph is in fact a subgraph of theGraph. - For each vertex v in graph G and subgraph H, we iterate the adjacency - list of H(v) and, for each neighbor w, we mark G(w). Then, we - iterate the adjacency list of G(v) and unmark each neighbor. Then, - we iterate the adjacency list of H(v) again to ensure that every - neighbor w was unmarked. If there exists a marked neighbor, then - H(v) contains an incident edge that is not incident to G(v). - - Returns TRUE if theSubgraph contains only edges from theGraph, - FALSE otherwise - ********************************************************************/ - -int _TestSubgraph(graphP theSubgraph, graphP theGraph) -{ -int v, e, degreeCount; -int Result = TRUE; -int invokeSortOnGraph = FALSE; -int invokeSortOnSubgraph = FALSE; - - // If the graph is not sorted by DFI, but the alleged subgraph is, - // then "unsort" the alleged subgraph so both have the same vertex order - if (!(theGraph->internalFlags & FLAGS_SORTEDBYDFI) && - (theSubgraph->internalFlags & FLAGS_SORTEDBYDFI)) - { - invokeSortOnSubgraph = TRUE; - gp_SortVertices(theSubgraph); - } - - // If the graph is not sorted by DFI, but the alleged subgraph is, - // then "unsort" the alleged subgraph so both have the same vertex order - if (!(theSubgraph->internalFlags & FLAGS_SORTEDBYDFI) && - (theGraph->internalFlags & FLAGS_SORTEDBYDFI)) - { - invokeSortOnGraph = TRUE; - gp_SortVertices(theGraph); - } - -/* We clear all visitation flags */ - - _ClearVertexVisitedFlags(theGraph, FALSE); - -/* For each vertex... */ - for (v = gp_GetFirstVertex(theSubgraph), degreeCount = 0; gp_VertexInRange(theSubgraph, v); v++) - { - /* For each neighbor w in the adjacency list of vertex v in the - subgraph, set the visited flag in w in the graph */ - - e = gp_GetFirstArc(theSubgraph, v); - while (gp_IsArc(e)) - { - if (gp_IsNotVertex(gp_GetNeighbor(theSubgraph, e))) - { - Result = FALSE; - break; - } - degreeCount++; - gp_SetVertexVisited(theGraph, gp_GetNeighbor(theSubgraph, e)); - e = gp_GetNextArc(theSubgraph, e); - } - - if (Result != TRUE) - break; - - /* For each neighbor w in the adjacency list of vertex v in the graph, - clear the visited flag in w in the graph */ - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_IsNotVertex(gp_GetNeighbor(theGraph, e))) - { - Result = FALSE; - break; - } - gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); - e = gp_GetNextArc(theGraph, e); - } - - if (Result != TRUE) - break; - - /* For each neighbor w in the adjacency list of vertex v in the subgraph, - ensure that the visited flag in w was cleared (otherwise, the "subgraph" - would incorrectly contain an adjacency not contained in the ("super") graph) */ - - e = gp_GetFirstArc(theSubgraph, v); - while (gp_IsArc(e)) - { - if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theSubgraph, e))) - { - Result = FALSE; - break; - } - e = gp_GetNextArc(theSubgraph, e); - } - - if (Result != TRUE) - break; - } - - // Restore the DFI sort order of either graph if it had to be reordered at the start - if (invokeSortOnSubgraph) - gp_SortVertices(theSubgraph); - if (invokeSortOnGraph) - gp_SortVertices(theGraph); - - // Assuming theSubgraph is a subgraph, we also do an extra integrity check to ensure - // proper edge array utilization - if (Result == TRUE) - { - // If the edge count is wrong, we fail the subgraph test in a way that invokes - // the name NOTOK so that in debug mode there is more trace on the failure. - if (degreeCount != 2*theSubgraph->M) - Result = NOTOK == FALSE ? NOTOK : FALSE; - } - - return Result; -} diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphUtils.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphUtils.c deleted file mode 100644 index 1cc18bce7..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/graphUtils.c +++ /dev/null @@ -1,2511 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wold-style-definition" -#endif - -#include - -#include "graphStructures.h" -#include "graph.h" - -/* Imported functions for FUNCTION POINTERS */ - -extern int _EmbeddingInitialize(graphP theGraph); -extern int _SortVertices(graphP theGraph); -extern void _EmbedBackEdgeToDescendant(graphP theGraph, int RootSide, int RootVertex, int W, int WPrevLink); -extern void _WalkUp(graphP theGraph, int v, int e); -extern int _WalkDown(graphP theGraph, int v, int RootVertex); -extern int _MergeBicomps(graphP theGraph, int v, int RootVertex, int W, int WPrevLink); -extern void _MergeVertex(graphP theGraph, int W, int WPrevLink, int R); -extern int _HandleBlockedBicomp(graphP theGraph, int v, int RootVertex, int R); -extern int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); -extern int _MarkDFSPath(graphP theGraph, int ancestor, int descendant); -extern int _EmbedPostprocess(graphP theGraph, int v, int edgeEmbeddingResult); -extern int _CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph); -extern int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph); -extern int _ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize); -extern int _WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize); - -/* Internal util functions for FUNCTION POINTERS */ - -int _HideVertex(graphP theGraph, int vertex); -void _HideEdge(graphP theGraph, int arcPos); -void _RestoreEdge(graphP theGraph, int arcPos); -int _ContractEdge(graphP theGraph, int e); -int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore); -int _RestoreVertex(graphP theGraph); - -/******************************************************************** - Private functions, except exported within library - ********************************************************************/ - -void _InitIsolatorContext(graphP theGraph); -void _ClearVisitedFlags(graphP theGraph); -void _ClearVertexVisitedFlags(graphP theGraph, int); -void _ClearEdgeVisitedFlags(graphP theGraph); -int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot); -int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot); -void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph); -int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue); -int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot); - -int _HideInternalEdges(graphP theGraph, int vertex); -int _RestoreInternalEdges(graphP theGraph, int stackBottom); -int _RestoreHiddenEdges(graphP theGraph, int stackBottom); - -int _GetBicompSize(graphP theGraph, int BicompRoot); -int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot); -int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot); - -void _InitFunctionTable(graphP theGraph); - -/******************************************************************** - Private functions. - ********************************************************************/ - -void _InitVertices(graphP theGraph); -void _InitEdges(graphP theGraph); - -void _ClearGraph(graphP theGraph); - -int _GetRandomNumber(int NMin, int NMax); - -/* Private functions for which there are FUNCTION POINTERS */ - -void _InitVertexRec(graphP theGraph, int v); -void _InitVertexInfo(graphP theGraph, int v); -void _InitEdgeRec(graphP theGraph, int e); - -int _InitGraph(graphP theGraph, int N); -void _ReinitializeGraph(graphP theGraph); -int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity); - -/******************************************************************** - gp_New() - Constructor for graph object. - Can create two graphs if restricted to no dynamic memory. - ********************************************************************/ - -graphP gp_New() -{ -graphP theGraph = (graphP) malloc(sizeof(baseGraphStructure)); - - if (theGraph != NULL) - { - theGraph->E = NULL; - theGraph->V = NULL; - theGraph->VI = NULL; - - theGraph->BicompRootLists = NULL; - theGraph->sortedDFSChildLists = NULL; - theGraph->theStack = NULL; - - theGraph->extFace = NULL; - - theGraph->edgeHoles = NULL; - - theGraph->extensions = NULL; - - _InitFunctionTable(theGraph); - - _ClearGraph(theGraph); - } - - return theGraph; -} - -/******************************************************************** - _InitFunctionTable() - - If you add functions to the function table, then they must be - initialized here, but you must also add the new function pointer - to the definition of the graphFunctionTable in graphFunctionTable.h - - Function headers for the functions used to initialize the table are - classified at the top of this file as either imported from other - compilation units (extern) or private to this compilation unit. - Search for FUNCTION POINTERS in this file to see where to add the - function header. - ********************************************************************/ - -void _InitFunctionTable(graphP theGraph) -{ - theGraph->functions.fpEmbeddingInitialize = _EmbeddingInitialize; - theGraph->functions.fpEmbedBackEdgeToDescendant = _EmbedBackEdgeToDescendant; - theGraph->functions.fpWalkUp = _WalkUp; - theGraph->functions.fpWalkDown = _WalkDown; - theGraph->functions.fpMergeBicomps = _MergeBicomps; - theGraph->functions.fpMergeVertex = _MergeVertex; - theGraph->functions.fpHandleBlockedBicomp = _HandleBlockedBicomp; - theGraph->functions.fpHandleInactiveVertex = _HandleInactiveVertex; - theGraph->functions.fpEmbedPostprocess = _EmbedPostprocess; - theGraph->functions.fpMarkDFSPath = _MarkDFSPath; - theGraph->functions.fpCheckEmbeddingIntegrity = _CheckEmbeddingIntegrity; - theGraph->functions.fpCheckObstructionIntegrity = _CheckObstructionIntegrity; - - theGraph->functions.fpInitGraph = _InitGraph; - theGraph->functions.fpReinitializeGraph = _ReinitializeGraph; - theGraph->functions.fpEnsureArcCapacity = _EnsureArcCapacity; - theGraph->functions.fpSortVertices = _SortVertices; - - theGraph->functions.fpReadPostprocess = _ReadPostprocess; - theGraph->functions.fpWritePostprocess = _WritePostprocess; - - theGraph->functions.fpHideEdge = _HideEdge; - theGraph->functions.fpRestoreEdge = _RestoreEdge; - theGraph->functions.fpHideVertex = _HideVertex; - theGraph->functions.fpRestoreVertex = _RestoreVertex; - theGraph->functions.fpContractEdge = _ContractEdge; - theGraph->functions.fpIdentifyVertices = _IdentifyVertices; -} - -/******************************************************************** - gp_InitGraph() - Allocates memory for vertex and edge records now that N is known. - The arcCapacity is set to (2 * DEFAULT_EDGE_LIMIT * N) unless it - has already been set by gp_EnsureArcCapacity() - - For V, we need 2N vertex records, N for vertices and N for virtual vertices (root copies). - - For VI, we need N vertexInfo records. - - For E, we need arcCapacity edge records. - - The BicompRootLists and sortedDFSChildLists are of size N and start out empty. - - The stack, initially empty, is made big enough for a pair of integers - per edge record (2 * arcCapacity), or 6N integers if the arcCapacity - was set below the default value. - - The edgeHoles stack, initially empty, is set to arcCapacity / 2, - which is big enough to push every edge (to indicate an edge - you only need to indicate one of its two edge records) - - Returns OK on success, NOTOK on all failures. - On NOTOK, graph extensions are freed so that the graph is - returned to the post-condition of gp_New(). - ********************************************************************/ - -int gp_InitGraph(graphP theGraph, int N) -{ - // valid params check - if (theGraph == NULL || N <= 0) - return NOTOK; - - // Should not call init a second time; use reinit - if (theGraph->N) - return NOTOK; - - return theGraph->functions.fpInitGraph(theGraph, N); -} - -int _InitGraph(graphP theGraph, int N) -{ - int Vsize, VIsize, Esize, stackSize; - - // Compute the vertex and edge capacities of the graph - theGraph->N = N; - theGraph->NV = N; - theGraph->arcCapacity = theGraph->arcCapacity > 0 ? theGraph->arcCapacity : 2*DEFAULT_EDGE_LIMIT*N; - VIsize = gp_PrimaryVertexIndexBound(theGraph); - Vsize = gp_VertexIndexBound(theGraph); - Esize = gp_EdgeIndexBound(theGraph); - - // Stack size is 2 integers per arc, or 6 integers per vertex in case of small arcCapacity - stackSize = 2 * Esize; - stackSize = stackSize < 6*N ? 6*N : stackSize; - - // Allocate memory as described above - if ((theGraph->V = (vertexRecP) calloc(Vsize, sizeof(vertexRec))) == NULL || - (theGraph->VI = (vertexInfoP) calloc(VIsize, sizeof(vertexInfo))) == NULL || - (theGraph->E = (edgeRecP) calloc(Esize, sizeof(edgeRec))) == NULL || - (theGraph->BicompRootLists = LCNew(VIsize)) == NULL || - (theGraph->sortedDFSChildLists = LCNew(VIsize)) == NULL || - (theGraph->theStack = sp_New(stackSize)) == NULL || - (theGraph->extFace = (extFaceLinkRecP) calloc(Vsize, sizeof(extFaceLinkRec))) == NULL || - (theGraph->edgeHoles = sp_New(Esize / 2)) == NULL || - 0) - { - _ClearGraph(theGraph); - return NOTOK; - } - - // Initialize memory - _InitVertices(theGraph); - _InitEdges(theGraph); - _InitIsolatorContext(theGraph); - - return OK; -} - -/******************************************************************** - _InitVertices() - ********************************************************************/ -void _InitVertices(graphP theGraph) -{ -#if NIL == 0 - memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec)); - memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo)); - memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); -#elif NIL == -1 - int v; - - memset(theGraph->V, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(vertexRec)); - memset(theGraph->VI, NIL_CHAR, gp_PrimaryVertexIndexBound(theGraph) * sizeof(vertexInfo)); - memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_InitVertexFlags(theGraph, v); - -#else - int v; - - // Initialize primary vertices - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - _InitVertexRec(theGraph, v); - _InitVertexInfo(theGraph, v); - gp_SetExtFaceVertex(theGraph, v, 0, NIL); - gp_SetExtFaceVertex(theGraph, v, 1, NIL); - } - - // Initialize virtual vertices - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - { - _InitVertexRec(theGraph, v); - gp_SetExtFaceVertex(theGraph, v, 0, NIL); - gp_SetExtFaceVertex(theGraph, v, 1, NIL); - } -#endif -} - -/******************************************************************** - _InitEdges() - ********************************************************************/ -void _InitEdges(graphP theGraph) -{ -#if NIL == 0 - memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); -#elif NIL == -1 - int e, Esize; - - memset(theGraph->E, NIL_CHAR, gp_EdgeIndexBound(theGraph) * sizeof(edgeRec)); - - Esize = gp_EdgeIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - gp_InitEdgeFlags(theGraph, e); - -#else - int e, Esize; - - Esize = gp_EdgeIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - _InitEdgeRec(theGraph, e); -#endif -} - -/******************************************************************** - gp_ReinitializeGraph() - Reinitializes a graph, restoring it to the state it was in immediately - after gp_InitGraph() processed it. - ********************************************************************/ - -void gp_ReinitializeGraph(graphP theGraph) -{ - if (theGraph == NULL || theGraph->N <= 0) - return; - - theGraph->functions.fpReinitializeGraph(theGraph); -} - -void _ReinitializeGraph(graphP theGraph) -{ - theGraph->M = 0; - theGraph->internalFlags = theGraph->embedFlags = 0; - - _InitVertices(theGraph); - _InitEdges(theGraph); - _InitIsolatorContext(theGraph); - - LCReset(theGraph->BicompRootLists); - LCReset(theGraph->sortedDFSChildLists); - sp_ClearStack(theGraph->theStack); - sp_ClearStack(theGraph->edgeHoles); -} - -/******************************************************************** - gp_GetArcCapacity() - Returns the arcCapacity of theGraph, which is twice the maximum - number of edges that can be added to the theGraph. - ********************************************************************/ -int gp_GetArcCapacity(graphP theGraph) -{ - return theGraph->arcCapacity - gp_GetFirstEdge(theGraph); -} - -/******************************************************************** - gp_EnsureArcCapacity() - This method ensures that theGraph is or will be capable of storing - at least requiredArcCapacity edge records. Two edge records are - needed per edge. - - This method is most performant when invoked immediately after - gp_New(), since it must only set the arcCapacity and then let - normal initialization to occur through gp_InitGraph(). - - This method is also a constant time operation if the graph already - has at least the requiredArcCapacity, since it will return OK - without making any structural changes. - - This method is generally more performant if it is invoked before - attaching extensions to the graph. Some extensions associate - parallel data with edge records, which is a faster operation if - the associated data is created and initialized only after the - proper arcCapacity is specified. - - If the graph has been initialized and has a lower arc capacity, - then the array of edge records is reallocated to satisfy the - requiredArcCapacity. The new array contains the old edges and - edge holes at the same locations, and all newly created edge records - are initialized. - - Also, if the arc capacity must be increased, then the - arcCapacity member of theGraph is changed and both - theStack and edgeHoles are expanded (since the sizes of both - are based on the arc capacity). - - Extensions that add to data associated with edges must overload - this method to ensure capacity in the parallel extension data - structures. An extension can return NOTOK if it does not - support arc capacity expansion. The extension function will - not be called if arcCapacity is expanded before the graph is - initialized, and it is assumed that extensions will allocate - parallel data structures according to the arc capacity. - - If an extension supports arc capacity expansion, then higher - performance can be obtained by using the method of unhooking - the initializers for individual edge records before invoking - the superclass version of fpEnsureArcCapacity(). Ideally, - application authors should ensure the proper arc capacity before - attaching extensions to achieve better performance. - - Returns NOTOK on failure to reallocate the edge record array to - satisfy the requiredArcCapacity, or if the requested - capacity is odd - OK if reallocation is not required or if reallocation succeeds - ********************************************************************/ -int gp_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ - if (theGraph == NULL || requiredArcCapacity <= 0) - return NOTOK; - - // Train callers to only ask for an even number of arcs, since - // two are required per edge or directed edge. - if (requiredArcCapacity & 1) - return NOTOK; - - if (theGraph->arcCapacity >= requiredArcCapacity) - return OK; - - // In the special case where gp_InitGraph() has not yet been called, - // we can simply set the higher arcCapacity since normal initialization - // will then allocate the correct number of edge records. - if (theGraph->N == 0) - { - theGraph->arcCapacity = requiredArcCapacity; - return OK; - } - - // Try to expand the arc capacity - return theGraph->functions.fpEnsureArcCapacity(theGraph, requiredArcCapacity); -} - -int _EnsureArcCapacity(graphP theGraph, int requiredArcCapacity) -{ -stackP newStack; -int e, Esize = gp_EdgeIndexBound(theGraph), - newEsize = gp_GetFirstEdge(theGraph) + requiredArcCapacity; - - // If the new size is less than or equal to the old size, then - // the graph already has the required arc capacity - if (newEsize <= Esize) - return OK; - - // Expand theStack - if (sp_GetCapacity(theGraph->theStack) < 2 * requiredArcCapacity) - { - int stackSize = 2 * requiredArcCapacity; - - if (stackSize < 6*theGraph->N) - { - // NOTE: Since this routine only makes the stack bigger, this - // calculation is not needed here because we already ensured - // we had stack capacity of the greater of 2*arcs and 6*N - // But we do it for clarity and consistency (e.g. so this rule - // is not forgotten whenever a "SetArcCapacity" method or a - // "reduceArcCapacity" method is added) - stackSize = 6*theGraph->N; - } - - if ((newStack = sp_New(stackSize)) == NULL) - return NOTOK; - - sp_CopyContent(newStack, theGraph->theStack); - sp_Free(&theGraph->theStack); - theGraph->theStack = newStack; - } - - // Expand edgeHoles - if ((newStack = sp_New(requiredArcCapacity / 2)) == NULL) { - return NOTOK; - } - - sp_CopyContent(newStack, theGraph->edgeHoles); - sp_Free(&theGraph->edgeHoles); - theGraph->edgeHoles = newStack; - - // Reallocate the edgeRec array to the new size, - theGraph->E = (edgeRecP) realloc(theGraph->E, newEsize*sizeof(edgeRec)); - if (theGraph->E == NULL) - return NOTOK; - - // Initialize the new edge records - for (e = Esize; e < newEsize; e++) - _InitEdgeRec(theGraph, e); - - // The new arcCapacity has been successfully achieved - theGraph->arcCapacity = requiredArcCapacity; - return OK; -} - -/******************************************************************** - _InitVertexRec() - Sets the fields in a single vertex record to initial values - ********************************************************************/ - -void _InitVertexRec(graphP theGraph, int v) -{ - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - gp_SetVertexIndex(theGraph, v, NIL); - gp_InitVertexFlags(theGraph, v); -} - -/******************************************************************** - _InitVertexInfo() - Sets the fields in a single vertex record to initial values - ********************************************************************/ - -void _InitVertexInfo(graphP theGraph, int v) -{ - gp_SetVertexParent(theGraph, v, NIL); - gp_SetVertexLeastAncestor(theGraph, v, NIL); - gp_SetVertexLowpoint(theGraph, v, NIL); - - gp_SetVertexVisitedInfo(theGraph, v, NIL); - gp_SetVertexPertinentEdge(theGraph, v, NIL); - gp_SetVertexPertinentRootsList(theGraph, v, NIL); - gp_SetVertexFuturePertinentChild(theGraph, v, NIL); - gp_SetVertexSortedDFSChildList(theGraph, v, NIL); - gp_SetVertexFwdArcList(theGraph, v, NIL); -} - -/******************************************************************** - _InitEdgeRec() - Sets the fields in a single edge record structure to initial values - ********************************************************************/ - -void _InitEdgeRec(graphP theGraph, int e) -{ - gp_SetNeighbor(theGraph, e, NIL); - gp_SetPrevArc(theGraph, e, NIL); - gp_SetNextArc(theGraph, e, NIL); - gp_InitEdgeFlags(theGraph, e); -} - -/******************************************************************** - _InitIsolatorContext() - ********************************************************************/ - -void _InitIsolatorContext(graphP theGraph) -{ -isolatorContextP IC = &theGraph->IC; - - IC->minorType = 0; - IC->v = IC->r = IC->x = IC->y = IC->w = IC->px = IC->py = IC->z = - IC->ux = IC->dx = IC->uy = IC->dy = IC->dw = IC->uz = IC->dz = NIL; -} - -/******************************************************************** - _ClearVisitedFlags() - ********************************************************************/ - -void _ClearVisitedFlags(graphP theGraph) -{ - _ClearVertexVisitedFlags(theGraph, TRUE); - _ClearEdgeVisitedFlags(theGraph); -} - -/******************************************************************** - _ClearVertexVisitedFlags() - ********************************************************************/ - -void _ClearVertexVisitedFlags(graphP theGraph, int includeVirtualVertices) -{ - int v; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_ClearVertexVisited(theGraph, v); - - if (includeVirtualVertices) - for (v = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, v); v++) - gp_ClearVertexVisited(theGraph, v); -} - -/******************************************************************** - _ClearEdgeVisitedFlags() - ********************************************************************/ - -void _ClearEdgeVisitedFlags(graphP theGraph) -{ - int e, EsizeOccupied; - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - gp_ClearEdgeVisited(theGraph, e); -} - -/******************************************************************** - _ClearVisitedFlagsInBicomp() - - Clears the visited flag of the vertices and arcs in the bicomp rooted - by BicompRoot. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most one integer per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _ClearVisitedFlagsInBicomp(graphP theGraph, int BicompRoot) -{ -int stackBottom = sp_GetCurrentSize(theGraph->theStack); -int v, e; - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, v); - gp_ClearVertexVisited(theGraph, v); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - gp_ClearEdgeVisited(theGraph, e); - - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _ClearVisitedFlagsInOtherBicomps() - Typically, we want to clear all visited flags in the graph - (see _ClearVisitedFlags). However, in some algorithms this would be - too costly, so it is necessary to clear the visited flags only - in one bicomp (see _ClearVisitedFlagsInBicomp), then do some processing - that sets some of the flags then performs some tests. If the tests - are positive, then we can clear all the visited flags in the - other bicomps (the processing may have set the visited flags in the - one bicomp in a particular way that we want to retain, so we skip - the given bicomp). - ********************************************************************/ - -int _ClearVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot) -{ - int R; - - for (R = gp_GetFirstVirtualVertex(theGraph); gp_VirtualVertexInRange(theGraph, R); R++) - { - if (R != BicompRoot && gp_VirtualVertexInUse(theGraph, R)) - { - if (_ClearVisitedFlagsInBicomp(theGraph, R) != OK) - return NOTOK; - } - } - return OK; -} - -/******************************************************************** - _ClearVisitedFlagsInUnembeddedEdges() - Unembedded edges aren't part of any bicomp yet, but it may be - necessary to clear their visited flags. - ********************************************************************/ - -void _ClearVisitedFlagsInUnembeddedEdges(graphP theGraph) -{ - int v, e; - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - { - e = gp_GetVertexFwdArcList(theGraph, v); - while (gp_IsArc(e)) - { - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - if (e == gp_GetVertexFwdArcList(theGraph, v)) - e = NIL; - } - } -} - -/**************************************************************************** - _ClearVisitedFlagsOnPath() - This method clears the visited flags on the vertices and edges on the path - (u, v, ..., w, x) in which all vertices except the endpoints u and x - are degree 2. This method avoids performing more than constant work at the - path endpoints u and x, so the total work is on the order of the path length. - - Returns OK on success, NOTOK on internal failure - ****************************************************************************/ - -int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x) -{ - int e, eTwin; - - // We want to exit u from e, but we get eTwin first here in order to avoid - // work, in case the degree of u is greater than 2. - eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u); - if (gp_IsNotArc(eTwin)) - return NOTOK; - e = gp_GetTwinArc(theGraph, eTwin); - - v = u; - - do { - // Mark the vertex and the exiting edge - gp_ClearVertexVisited(theGraph, v); - gp_ClearEdgeVisited(theGraph, e); - gp_ClearEdgeVisited(theGraph, eTwin); - - // Get the next vertex - v = gp_GetNeighbor(theGraph, e); - e = gp_GetNextArcCircular(theGraph, eTwin); - eTwin = gp_GetTwinArc(theGraph, e); - } while (v != x); - - // Mark the last vertex with 'visited' - gp_ClearVertexVisited(theGraph, x); - - return OK; -} - -/**************************************************************************** - _SetVisitedFlagsOnPath() - This method sets the visited flags on the vertices and edges on the path - (u, v, ..., w, x) in which all vertices except the endpoints u and x - are degree 2. This method avoids performing more than constant work at the - path endpoints u and x, so the total work is on the order of the path length. - - Returns OK on success, NOTOK on internal failure - ****************************************************************************/ - -int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x) -{ - int e, eTwin; - - // We want to exit u from e, but we get eTwin first here in order to avoid - // work, in case the degree of u is greater than 2. - eTwin = gp_GetNeighborEdgeRecord(theGraph, v, u); - if (gp_IsNotArc(eTwin)) - return NOTOK; - e = gp_GetTwinArc(theGraph, eTwin); - - v = u; - - do { - // Mark the vertex and the exiting edge - gp_SetVertexVisited(theGraph, v); - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, eTwin); - - // Get the next vertex - v = gp_GetNeighbor(theGraph, e); - e = gp_GetNextArcCircular(theGraph, eTwin); - eTwin = gp_GetTwinArc(theGraph, e); - } while (v != x); - - // Mark the last vertex with 'visited' - gp_SetVertexVisited(theGraph, x); - - return OK; -} - -/******************************************************************** - _FillVertexVisitedInfoInBicomp() - - Places the FillValue into the visitedInfo of the non-virtual vertices - in the bicomp rooted by BicompRoot. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most one integer per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _FillVertexVisitedInfoInBicomp(graphP theGraph, int BicompRoot, int FillValue) -{ -int v, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, v); - - if (gp_IsNotVirtualVertex(theGraph, v)) - gp_SetVertexVisitedInfo(theGraph, v, FillValue); - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _ClearVertexTypeInBicomp() - - Clears the 'obstruction type' bits for each vertex in the bicomp - rooted by BicompRoot. - - This method uses the stack but preserves whatever may have been - on it. In debug mode, it will return NOTOK if the stack overflows. - This method pushes at most one integer per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure. - ********************************************************************/ - -int _ClearVertexTypeInBicomp(graphP theGraph, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - gp_ClearVertexObstructionType(theGraph, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _ClearGraph() - Clears all memory used by the graph, restoring it to the state it - was in immediately after gp_New() created it. - ********************************************************************/ - -void _ClearGraph(graphP theGraph) -{ - if (theGraph->V != NULL) - { - free(theGraph->V); - theGraph->V = NULL; - } - if (theGraph->VI != NULL) - { - free(theGraph->VI); - theGraph->V = NULL; - } - if (theGraph->E != NULL) - { - free(theGraph->E); - theGraph->E = NULL; - } - - theGraph->N = 0; - theGraph->NV = 0; - theGraph->M = 0; - theGraph->arcCapacity = 0; - theGraph->internalFlags = 0; - theGraph->embedFlags = 0; - - _InitIsolatorContext(theGraph); - - LCFree(&theGraph->BicompRootLists); - LCFree(&theGraph->sortedDFSChildLists); - - sp_Free(&theGraph->theStack); - - if (theGraph->extFace != NULL) - { - free(theGraph->extFace); - theGraph->extFace = NULL; - } - - sp_Free(&theGraph->edgeHoles); - - gp_FreeExtensions(theGraph); -} - -/******************************************************************** - gp_Free() - Frees G and V, then the graph record. Then sets your pointer to NULL - (so you must pass the address of your pointer). - ********************************************************************/ - -void gp_Free(graphP *pGraph) -{ - if (pGraph == NULL) return; - if (*pGraph == NULL) return; - - _ClearGraph(*pGraph); - - free(*pGraph); - *pGraph = NULL; -} - -/******************************************************************** - gp_CopyAdjacencyLists() - Copies the adjacency lists from the srcGraph to the dstGraph. - This method intentionally copies only the adjacency lists of the - first N vertices, so the adjacency lists of virtual vertices are - excluded (unless the caller temporarily resets the value of N to - include NV). - - Returns OK on success, NOTOK on failures, e.g. if the two graphs - have different orders N or if the arcCapacity of dstGraph cannot - be increased to match that of srcGraph. - ********************************************************************/ -int gp_CopyAdjacencyLists(graphP dstGraph, graphP srcGraph) -{ - int v, e, EsizeOccupied; - - if (dstGraph == NULL || srcGraph == NULL) - return NOTOK; - - if (dstGraph->N != srcGraph->N || dstGraph->N == 0) - return NOTOK; - - if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK) - return NOTOK; - - // Copy the links that hook each owning vertex to its adjacency list - for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++) - { - gp_SetFirstArc(dstGraph, v, gp_GetFirstArc(srcGraph, v)); - gp_SetLastArc(dstGraph, v, gp_GetLastArc(srcGraph, v)); - } - - // Copy the adjacency links and neighbor pointers for each arc - EsizeOccupied = gp_EdgeInUseIndexBound(srcGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e++) - { - gp_SetNeighbor(dstGraph, e, gp_GetNeighbor(srcGraph, e)); - gp_SetNextArc(dstGraph, e, gp_GetNextArc(srcGraph, e)); - gp_SetPrevArc(dstGraph, e, gp_GetPrevArc(srcGraph, e)); - } - - // Tell the dstGraph how many edges it now has and where the edge holes are - dstGraph->M = srcGraph->M; - sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles); - - return OK; -} - -/******************************************************************** - gp_CopyGraph() - Copies the content of the srcGraph into the dstGraph. The dstGraph - must have been previously initialized with the same number of - vertices as the srcGraph (e.g. gp_InitGraph(dstGraph, srcGraph->N). - - Returns OK for success, NOTOK for failure. - ********************************************************************/ - -int gp_CopyGraph(graphP dstGraph, graphP srcGraph) -{ -int v, e, Esize; - - // Parameter checks - if (dstGraph == NULL || srcGraph == NULL) - { - return NOTOK; - } - - // The graphs need to be the same order and initialized - if (dstGraph->N != srcGraph->N || dstGraph->N == 0) - { - return NOTOK; - } - - // Ensure dstGraph has the required arc capacity; this expands - // dstGraph if needed, but does not contract. An error is only - // returned if the expansion fails. - if (gp_EnsureArcCapacity(dstGraph, srcGraph->arcCapacity) != OK) - { - return NOTOK; - } - - // Copy the primary vertices. Augmentations to vertices created - // by extensions are copied below by gp_CopyExtensions() - for (v = gp_GetFirstVertex(srcGraph); gp_VertexInRange(srcGraph, v); v++) - { - gp_CopyVertexRec(dstGraph, v, srcGraph, v); - gp_CopyVertexInfo(dstGraph, v, srcGraph, v); - gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0)); - gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1)); - } - - // Copy the virtual vertices. Augmentations to virtual vertices created - // by extensions are copied below by gp_CopyExtensions() - for (v = gp_GetFirstVirtualVertex(srcGraph); gp_VirtualVertexInRange(srcGraph, v); v++) - { - gp_CopyVertexRec(dstGraph, v, srcGraph, v); - gp_SetExtFaceVertex(dstGraph, v, 0, gp_GetExtFaceVertex(srcGraph, v, 0)); - gp_SetExtFaceVertex(dstGraph, v, 1, gp_GetExtFaceVertex(srcGraph, v, 1)); - } - - // Copy the basic EdgeRec structures. Augmentations to the edgeRec structure - // created by extensions are copied below by gp_CopyExtensions() - Esize = gp_EdgeIndexBound(srcGraph); - for (e = gp_GetFirstEdge(theGraph); e < Esize; e++) - gp_CopyEdgeRec(dstGraph, e, srcGraph, e); - - // Give the dstGraph the same size and intrinsic properties - dstGraph->N = srcGraph->N; - dstGraph->NV = srcGraph->NV; - dstGraph->M = srcGraph->M; - dstGraph->internalFlags = srcGraph->internalFlags; - dstGraph->embedFlags = srcGraph->embedFlags; - - dstGraph->IC = srcGraph->IC; - - LCCopy(dstGraph->BicompRootLists, srcGraph->BicompRootLists); - LCCopy(dstGraph->sortedDFSChildLists, srcGraph->sortedDFSChildLists); - sp_Copy(dstGraph->theStack, srcGraph->theStack); - sp_Copy(dstGraph->edgeHoles, srcGraph->edgeHoles); - - // Copy the set of extensions, which includes copying the - // extension data as well as the function overload tables - if (gp_CopyExtensions(dstGraph, srcGraph) != OK) - return NOTOK; - - // Copy the graph's function table, which has the pointers to - // the most recent extension overloads of each function (or - // the original function pointer if a particular function has - // not been overloaded). - // This must be done after copying the extension because the - // first step of copying the extensions is to delete the - // dstGraph extensions, which clears its function table. - // Therefore, no good to assign the srcGraph functions *before* - // copying the extensions because the assignment would be wiped out - // This, in turn, means that the DupContext function of an extension - // *cannot* depend on any extension function overloads; the extension - // must directly invoke extension functions only. - dstGraph->functions = srcGraph->functions; - - return OK; -} - -/******************************************************************** - gp_DupGraph() - ********************************************************************/ - -graphP gp_DupGraph(graphP theGraph) -{ -graphP result; - - if ((result = gp_New()) == NULL) return NULL; - - if (gp_InitGraph(result, theGraph->N) != OK || - gp_CopyGraph(result, theGraph) != OK) - { - gp_Free(&result); - return NULL; - } - - return result; -} - -/******************************************************************** - gp_CreateRandomGraph() - - Creates a randomly generated graph. First a tree is created by - connecting each vertex to some successor. Then a random number of - additional random edges are added. If an edge already exists, then - we retry until a non-existent edge is picked. - - This function assumes the caller has already called srand(). - - Returns OK on success, NOTOK on failure - ********************************************************************/ - -int gp_CreateRandomGraph(graphP theGraph) -{ -int N, M, u, v, m; - - N = theGraph->N; - -/* Generate a random tree; note that this method virtually guarantees - that the graph will be renumbered, but it is linear time. - Also, we are not generating the DFS tree but rather a tree - that simply ensures the resulting random graph is connected. */ - - for (v = gp_GetFirstVertex(theGraph)+1; gp_VertexInRange(theGraph, v); v++) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v-1); - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - } - -/* Generate a random number of additional edges - (actually, leave open a small chance that no - additional edges will be added). */ - - M = _GetRandomNumber(7*N/8, theGraph->arcCapacity/2); - - if (M > N*(N-1)/2) - M = N*(N-1)/2; - - for (m = N-1; m < M; m++) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)-1); - v = _GetRandomNumber(u+1, gp_GetLastVertex(theGraph)); - - // If the edge (u,v) exists, decrement eIndex to try again - if (gp_IsNeighbor(theGraph, u, v)) - m--; - - // If the edge (u,v) doesn't exist, add it - else - { - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - } - } - - return OK; -} - -/******************************************************************** - _GetRandomNumber() - This function generates a random number between NMin and NMax - inclusive. It assumes that the caller has called srand(). - It calls rand(), but before truncating to the proper range, - it adds the high bits of the rand() result into the low bits. - The result of this is that the randomness appearing in the - truncated bits also has an affect on the non-truncated bits. - ********************************************************************/ - -int _GetRandomNumber(int NMin, int NMax) -{ -int N = rand(); - - if (NMax < NMin) return NMin; - - N += ((N&0xFFFF0000)>>16); - N += ((N&0x0000FF00)>>8); - N &= 0x7FFFFFF; - N %= (NMax-NMin+1); - return N+NMin; -} - -/******************************************************************** - _getUnprocessedChild() - Support routine for gp_Create RandomGraphEx(), this function - obtains a child of the given vertex in the randomly generated - tree that has not yet been processed. NIL is returned if the - given vertex has no unprocessed children - - ********************************************************************/ - -int _getUnprocessedChild(graphP theGraph, int parent) -{ -int e = gp_GetFirstArc(theGraph, parent); -int eTwin = gp_GetTwinArc(theGraph, e); -int child = gp_GetNeighbor(theGraph, e); - - // The tree edges were added to the beginning of the adjacency list, - // and we move processed tree edge records to the end of the list, - // so if the immediate next arc (edge record) is not a tree edge - // then we return NIL because the vertex has no remaining - // unprocessed children - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED) - return NIL; - - // If the child has already been processed, then all children - // have been pushed to the end of the list, and we have just - // encountered the first child we processed, so there are no - // remaining unprocessed children */ - if (gp_GetEdgeVisited(theGraph, e)) - return NIL; - - // We have found an edge leading to an unprocessed child, so - // we mark it as processed so that it doesn't get returned - // again in future iterations. - gp_SetEdgeVisited(theGraph, e); - gp_SetEdgeVisited(theGraph, eTwin); - - // Now we move the edge record in the parent vertex to the end - // of the adjacency list of that vertex. - gp_MoveArcToLast(theGraph, parent, e); - - // Now we move the edge record in the child vertex to the - // end of the adjacency list of the child. - gp_MoveArcToLast(theGraph, child, eTwin); - - // Now we set the child's parent and return the child. - gp_SetVertexParent(theGraph, child, parent); - - return child; -} - -/******************************************************************** - _hasUnprocessedChild() - Support routine for gp_Create RandomGraphEx(), this function - obtains a child of the given vertex in the randomly generated - tree that has not yet been processed. False (0) is returned - unless the given vertex has an unprocessed child. - ********************************************************************/ - -int _hasUnprocessedChild(graphP theGraph, int parent) -{ -int e = gp_GetFirstArc(theGraph, parent); - - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_NOTDEFINED) - return 0; - - if (gp_GetEdgeVisited(theGraph, e)) - return 0; - - return 1; -} - -/******************************************************************** - gp_CreateRandomGraphEx() - Given a graph structure with a pre-specified number of vertices N, - this function creates a graph with the specified number of edges. - - If numEdges <= 3N-6, then the graph generated is planar. If - numEdges is larger, then a maximal planar graph is generated, then - (numEdges - 3N + 6) additional random edges are added. - - This function assumes the caller has already called srand(). - ********************************************************************/ - -int gp_CreateRandomGraphEx(graphP theGraph, int numEdges) -{ -int N, arc, M, root, v, c, p, last, u, e, EsizeOccupied; - - N = theGraph->N; - - if (numEdges > theGraph->arcCapacity/2) - numEdges = theGraph->arcCapacity/2; - -/* Generate a random tree. */ - - for (v = gp_GetFirstVertex(theGraph)+1; gp_VertexInRange(theGraph, v); v++) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), v-1); - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - - else - { - arc = gp_GetNeighborEdgeRecord(theGraph, u, v); - gp_SetEdgeType(theGraph, arc, EDGE_TYPE_RANDOMTREE); - gp_SetEdgeType(theGraph, gp_GetTwinArc(theGraph, arc), EDGE_TYPE_RANDOMTREE); - gp_ClearEdgeVisited(theGraph, arc); - gp_ClearEdgeVisited(theGraph, gp_GetTwinArc(theGraph, arc)); - } - } - -/* Add edges up to the limit or until the graph is maximal planar. */ - - M = numEdges <= 3*N - 6 ? numEdges : 3*N - 6; - - root = gp_GetFirstVertex(theGraph); - v = last = _getUnprocessedChild(theGraph, root); - - while (v != root && theGraph->M < M) - { - c = _getUnprocessedChild(theGraph, v); - - if (gp_IsVertex(c)) - { - if (last != v) - { - if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) - return NOTOK; - } - - if (gp_AddEdge(theGraph, root, 1, c, 1) != OK) - return NOTOK; - - v = last = c; - } - - else - { - p = gp_GetVertexParent(theGraph, v); - while (gp_IsVertex(p) && gp_IsNotVertex(c = _getUnprocessedChild(theGraph, p))) - { - v = p; - p = gp_GetVertexParent(theGraph, v); - if (gp_IsVertex(p) && p != root) - { - if (gp_AddEdge(theGraph, last, 1, p, 1) != OK) - return NOTOK; - } - } - - if (gp_IsVertex(p)) - { - if (p == root) - { - if (gp_AddEdge(theGraph, v, 1, c, 1) != OK) - return NOTOK; - - if (v != last) - { - if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) - return NOTOK; - } - } - else - { - if (gp_AddEdge(theGraph, last, 1, c, 1) != OK) - return NOTOK; - } - - if (p != root) - { - if (gp_AddEdge(theGraph, root, 1, c, 1) != OK) - return NOTOK; - last = c; - } - - v = c; - } - } - } - -/* Add additional edges if the limit has not yet been reached. */ - - while (theGraph->M < numEdges) - { - u = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)); - v = _GetRandomNumber(gp_GetFirstVertex(theGraph), gp_GetLastVertex(theGraph)); - - if (u != v && !gp_IsNeighbor(theGraph, u, v)) - if (gp_AddEdge(theGraph, u, 0, v, 0) != OK) - return NOTOK; - } - -/* Clear the edge types back to 'unknown' */ - - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = 0; e < EsizeOccupied; e++) - { - gp_ClearEdgeType(theGraph, e); - gp_ClearEdgeVisited(theGraph, e); - } - -/* Put all DFSParent indicators back to NIL */ - - for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_SetVertexParent(theGraph, v, NIL); - - return OK; -} - -/******************************************************************** - gp_IsNeighbor() - - Checks whether v is already in u's adjacency list, i.e. does the arc - u -> v exist. - If there is an edge record for v in u's list, but it is marked INONLY, - then it represents the arc v->u but not u->v, so it is ignored. - - Returns TRUE or FALSE. - ********************************************************************/ - -int gp_IsNeighbor(graphP theGraph, int u, int v) -{ -int e = gp_GetFirstArc(theGraph, u); - - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) == v) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) - return TRUE; - } - e = gp_GetNextArc(theGraph, e); - } - return FALSE; -} - -/******************************************************************** - gp_GetNeighborEdgeRecord() - Searches the adjacency list of u to obtains the edge record for v. - - NOTE: The caller should check whether the edge record is INONLY; - This method returns any edge record representing a connection - between vertices u and v, so this method can return an - edge record even if gp_IsNeighbor(theGraph, u, v) is false (0). - To filter out INONLY edge records, use gp_GetDirection() on - the edge record returned by this method. - - Returns NIL if there is no edge record indicating v in u's adjacency - list, or the edge record location otherwise. - ********************************************************************/ - -int gp_GetNeighborEdgeRecord(graphP theGraph, int u, int v) -{ -int e; - - if (gp_IsNotVertex(u) || gp_IsNotVertex(v)) - return NIL + NOTOK - NOTOK; - - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (gp_GetNeighbor(theGraph, e) == v) - return e; - - e = gp_GetNextArc(theGraph, e); - } - return NIL; -} - -/******************************************************************** - gp_GetVertexDegree() - - Counts the number of edge records in the adjacency list of a given - vertex V. - - Note: For digraphs, this method returns the total degree of the - vertex, including outward arcs (undirected and OUTONLY) - as well as INONLY arcs. Other functions are defined to get - the in-degree or out-degree of the vertex. - - Note: This function determines the degree by counting. An extension - could cache the degree value of each vertex and update the - cached value as edges are added and deleted. - ********************************************************************/ - -int gp_GetVertexDegree(graphP theGraph, int v) -{ -int e, degree; - - if (theGraph==NULL || gp_IsNotVertex(v)) - return 0 + NOTOK - NOTOK; - - degree = 0; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - degree++; - e = gp_GetNextArc(theGraph, e); - } - - return degree; -} - -/******************************************************************** - gp_GetVertexInDegree() - - Counts the number of edge records in the adjacency list of a given - vertex V that represent arcs from another vertex into V. - This includes undirected edges and INONLY arcs, so it only excludes - edges records that are marked as OUTONLY arcs. - - Note: This function determines the in-degree by counting. An extension - could cache the in-degree value of each vertex and update the - cached value as edges are added and deleted. - ********************************************************************/ - -int gp_GetVertexInDegree(graphP theGraph, int v) -{ -int e, degree; - - if (theGraph==NULL || gp_IsNotVertex(v)) - return 0 + NOTOK - NOTOK; - - degree = 0; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_OUTONLY) - degree++; - e = gp_GetNextArc(theGraph, e); - } - - return degree; -} - -/******************************************************************** - gp_GetVertexOutDegree() - - Counts the number of edge records in the adjacency list of a given - vertex V that represent arcs from V to another vertex. - This includes undirected edges and OUTONLY arcs, so it only excludes - edges records that are marked as INONLY arcs. - - Note: This function determines the out-degree by counting. An extension - could cache the out-degree value of each vertex and update the - cached value as edges are added and deleted. - ********************************************************************/ - -int gp_GetVertexOutDegree(graphP theGraph, int v) -{ -int e, degree; - - if (theGraph==NULL || gp_IsNotVertex(v)) - return 0 + NOTOK - NOTOK; - - degree = 0; - - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetDirection(theGraph, e) != EDGEFLAG_DIRECTION_INONLY) - degree++; - e = gp_GetNextArc(theGraph, e); - } - - return degree; -} - -/******************************************************************** - gp_AttachArc() - - This routine adds newArc into v's adjacency list at a position - adjacent to the edge record for e, either before or after e, - depending on link. If e is not an arc (e.g. if e is NIL), - then link is assumed to indicate whether the new arc is to be - placed at the beginning or end of v's adjacency list. - - NOTE: The caller can pass NIL for v if e is not NIL, since the - vertex is implied (gp_GetNeighbor(theGraph, eTwin)) - - The arc is assumed to already exist in the data structure (i.e. - the storage of edges), as only a whole edge (two arcs) can be - inserted into or deleted from the data structure. Hence there is - no such thing as gp_InsertArc() or gp_DeleteArc(). - - See also gp_DetachArc(), gp_InsertEdge() and gp_DeleteEdge() - ********************************************************************/ - -void gp_AttachArc(graphP theGraph, int v, int e, int link, int newArc) -{ - if (gp_IsArc(e)) - { - int e2 = gp_GetAdjacentArc(theGraph, e, link); - - // e's link is newArc, and newArc's 1^link is e - gp_SetAdjacentArc(theGraph, e, link, newArc); - gp_SetAdjacentArc(theGraph, newArc, 1^link, e); - - // newArcs's link is e2 - gp_SetAdjacentArc(theGraph, newArc, link, e2); - - // if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc - if (gp_IsArc(e2)) - gp_SetAdjacentArc(theGraph, e2, 1^link, newArc); - else - gp_SetArc(theGraph, v, 1^link, newArc); - } - else - { - int e2 = gp_GetArc(theGraph, v, link); - - // v's link is newArc, and newArc's 1^link is NIL - gp_SetArc(theGraph, v, link, newArc); - gp_SetAdjacentArc(theGraph, newArc, 1^link, NIL); - - // newArcs's elink is e2 - gp_SetAdjacentArc(theGraph, newArc, link, e2); - - // if e2 is an arc, then e2's 1^link is newArc, else v's 1^link is newArc - if (gp_IsArc(e2)) - gp_SetAdjacentArc(theGraph, e2, 1^link, newArc); - else - gp_SetArc(theGraph, v, 1^link, newArc); - } -} - -/**************************************************************************** - gp_DetachArc() - - This routine detaches arc from its adjacency list, but it does not delete - it from the data structure (only a whole edge can be deleted). - - Some algorithms must temporarily detach an edge, perform some calculation, - and eventually put the edge back. This routine supports that operation. - The neighboring adjacency list nodes are cross-linked, but the two link - members of the arc are retained, so the arc can be reattached later by - invoking _RestoreArc(). A sequence of detached arcs can only be restored - in the exact opposite order of their detachment. Thus, algorithms do not - directly use this method to implement the temporary detach/restore method. - Instead, gp_HideEdge() and gp_RestoreEdge are used, and algorithms push - edge hidden edge onto the stack. One example of this stack usage is - provided by detaching edges with gp_ContractEdge() or gp_IdentifyVertices(), - and reattaching with gp_RestoreIdentifications(), which unwinds the stack - by invoking gp_RestoreVertex(). - ****************************************************************************/ - -void gp_DetachArc(graphP theGraph, int arc) -{ - int nextArc = gp_GetNextArc(theGraph, arc), - prevArc = gp_GetPrevArc(theGraph, arc); - - if (gp_IsArc(nextArc)) - gp_SetPrevArc(theGraph, nextArc, prevArc); - else - gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), prevArc); - - if (gp_IsArc(prevArc)) - gp_SetNextArc(theGraph, prevArc, nextArc); - else - gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), nextArc); -} - -/******************************************************************** - gp_AddEdge() - Adds the undirected edge (u,v) to the graph by placing edge records - representing u into v's circular edge record list and v into u's - circular edge record list. - - upos receives the location in G where the u record in v's list will be - placed, and vpos is the location in G of the v record we placed in - u's list. These are used to initialize the short circuit links. - - ulink (0|1) indicates whether the edge record to v in u's list should - become adjacent to u by its 0 or 1 link, i.e. u[ulink] == vpos. - vlink (0|1) indicates whether the edge record to u in v's list should - become adjacent to v by its 0 or 1 link, i.e. v[vlink] == upos. - - ********************************************************************/ - -int gp_AddEdge(graphP theGraph, int u, int ulink, int v, int vlink) -{ -int upos, vpos; - - if (theGraph==NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || - !gp_VirtualVertexInRange(theGraph, u) || !gp_VirtualVertexInRange(theGraph, v)) - return NOTOK; - - /* We enforce the edge limit */ - - if (theGraph->M >= theGraph->arcCapacity/2) - return NONEMBEDDABLE; - - if (sp_NonEmpty(theGraph->edgeHoles)) - { - sp_Pop(theGraph->edgeHoles, vpos); - } - else - vpos = gp_EdgeInUseIndexBound(theGraph); - - upos = gp_GetTwinArc(theGraph, vpos); - - gp_SetNeighbor(theGraph, upos, v); - gp_AttachArc(theGraph, u, NIL, ulink, upos); - gp_SetNeighbor(theGraph, vpos, u); - gp_AttachArc(theGraph, v, NIL, vlink, vpos); - - theGraph->M++; - return OK; -} - -/******************************************************************** - gp_InsertEdge() - - This function adds the edge (u, v) such that the edge record added - to the adjacency list of u is adjacent to e_u and the edge record - added to the adjacency list of v is adjacent to e_v. - The direction of adjacency is given by e_ulink for e_u and e_vlink - for e_v. Specifically, the new edge will be comprised of two arcs, - n_u and n_v. In u's (v's) adjacency list, n_u (n_v) will be added - so that it is indicated by e_u's (e_v's) e_ulink (e_vlink). - If e_u (or e_v) is not an arc, then e_ulink (e_vlink) indicates - whether to prepend or append to the adjacency list for u (v). - ********************************************************************/ - -int gp_InsertEdge(graphP theGraph, int u, int e_u, int e_ulink, - int v, int e_v, int e_vlink) -{ -int vertMax = gp_GetLastVirtualVertex(theGraph), - edgeMax = gp_EdgeInUseIndexBound(theGraph) - 1, - upos, vpos; - - if (theGraph==NULL || u < gp_GetFirstVertex(theGraph) || v < gp_GetFirstVertex(theGraph) || - u > vertMax || v > vertMax || - e_u > edgeMax || (e_u < gp_GetFirstEdge(theGraph) && gp_IsArc(e_u)) || - e_v > edgeMax || (e_v < gp_GetFirstEdge(theGraph) && gp_IsArc(e_v)) || - e_ulink < 0 || e_ulink > 1 || e_vlink < 0 || e_vlink > 1) - return NOTOK; - - if (theGraph->M >= theGraph->arcCapacity/2) - return NONEMBEDDABLE; - - if (sp_NonEmpty(theGraph->edgeHoles)) - { - sp_Pop(theGraph->edgeHoles, vpos); - } - else - vpos = gp_EdgeInUseIndexBound(theGraph); - - upos = gp_GetTwinArc(theGraph, vpos); - - gp_SetNeighbor(theGraph, upos, v); - gp_AttachArc(theGraph, u, e_u, e_ulink, upos); - - gp_SetNeighbor(theGraph, vpos, u); - gp_AttachArc(theGraph, v, e_v, e_vlink, vpos); - - theGraph->M++; - - return OK; -} - -/**************************************************************************** - gp_DeleteEdge() - - This function deletes the given edge record e and its twin, reducing the - number of edges M in the graph. - Before the e^th record is deleted, its 'nextLink' adjacency list neighbor - is collected as the return result. This is useful when iterating through - an edge list and making deletions because the nextLink arc is the 'next' - arc in the iteration, but it is hard to obtain *after* deleting e. - ****************************************************************************/ - -int gp_DeleteEdge(graphP theGraph, int e, int nextLink) -{ - // Calculate the nextArc after e so that, when e is deleted, the return result - // informs a calling loop of the next edge to be processed. - int nextArc = gp_GetAdjacentArc(theGraph, e, nextLink); - - // Delete the edge records e and eTwin from their adjacency lists. - gp_DetachArc(theGraph, e); - gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e)); - - // Clear the two edge records - // (the bit twiddle (e & ~1) chooses the lesser of e and its twin arc) -#if NIL == 0 - memset(theGraph->E + (e & ~1), NIL_CHAR, sizeof(edgeRec) << 1); -#else - _InitEdgeRec(theGraph, e); - _InitEdgeRec(theGraph, gp_GetTwinArc(theGraph, e)); -#endif - - // Now we reduce the number of edges in the data structure - theGraph->M--; - - // If records e and eTwin were not the last in the edge record array, - // then record a new hole in the edge array. */ - if (e < gp_EdgeInUseIndexBound(theGraph)) - { - sp_Push(theGraph->edgeHoles, e); - } - - // Return the previously calculated successor of e. - return nextArc; -} - -/******************************************************************** - _RestoreArc() - This routine reinserts an arc into the edge list from which it - was previously removed by gp_DetachArc(). - - The assumed processing model is that arcs will be restored in reverse - of the order in which they were hidden, i.e. it is assumed that the - hidden arcs will be pushed on a stack and the arcs will be popped - from the stack for restoration. - ********************************************************************/ - -void _RestoreArc(graphP theGraph, int arc) -{ -int nextArc = gp_GetNextArc(theGraph, arc), - prevArc = gp_GetPrevArc(theGraph, arc); - - if (gp_IsArc(nextArc)) - gp_SetPrevArc(theGraph, nextArc, arc); - else - gp_SetLastArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc); - - if (gp_IsArc(prevArc)) - gp_SetNextArc(theGraph, prevArc, arc); - else - gp_SetFirstArc(theGraph, gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, arc)), arc); -} - -/******************************************************************** - gp_HideEdge() - This routine removes the two arcs of an edge from the adjacency lists - of its endpoint vertices, but does not delete them from the storage - data structure. - - Many algorithms must temporarily remove an edge, perform some - calculation, and eventually put the edge back. This routine supports - that operation. - - For each arc, the neighboring adjacency list nodes are cross-linked, - but the links in the arc are retained because they indicate the - neighbor arcs to which the arc can be reattached by gp_RestoreEdge(). - ********************************************************************/ - -void gp_HideEdge(graphP theGraph, int e) -{ - theGraph->functions.fpHideEdge(theGraph, e); -} - -void _HideEdge(graphP theGraph, int e) -{ - gp_DetachArc(theGraph, e); - gp_DetachArc(theGraph, gp_GetTwinArc(theGraph, e)); -} - -/******************************************************************** - gp_RestoreEdge() - This routine reinserts two two arcs of an edge into the adjacency - lists of the edge's endpoints, the arcs having been previously - removed by gp_HideEdge(). - - The assumed processing model is that edges will be restored in - reverse of the order in which they were hidden, i.e. it is assumed - that the hidden edges will be pushed on a stack and the edges will - be popped from the stack for restoration. - - Note: Since both arcs of an edge are restored, only one arc need - be pushed on the stack for restoration. This routine - restores the two arcs in the opposite order from the order - in which they are hidden by gp_HideEdge(). - ********************************************************************/ - -void gp_RestoreEdge(graphP theGraph, int e) -{ - theGraph->functions.fpRestoreEdge(theGraph, e); -} - -void _RestoreEdge(graphP theGraph, int e) -{ - _RestoreArc(theGraph, gp_GetTwinArc(theGraph, e)); - _RestoreArc(theGraph, e); -} - -/******************************************************************** - _HideInternalEdges() - Pushes onto the graph's stack and hides all arc nodes of the vertex - except the first and last arcs in the adjacency list of the vertex. - This method is typically called on a vertex that is on the external - face of a biconnected component, because the first and last arcs are - the ones that attach the vertex to the external face cycle, and any - other arcs in the adjacency list are inside that cycle. - - This method uses the stack. The caller is expected to clear the stack - or save the stack size before invocation, since the stack size is - needed to _RestoreInternalEdges(). - ********************************************************************/ - -int _HideInternalEdges(graphP theGraph, int vertex) -{ -int e = gp_GetFirstArc(theGraph, vertex); - - // If the vertex adjacency list is empty or if it contains - // only one edge, then there are no *internal* edges to hide - if (e == gp_GetLastArc(theGraph, vertex)) - return OK; - - // Start with the first internal edge - e = gp_GetNextArc(theGraph, e); - - // Cycle through all the edges, pushing each except stop - // before pushing the last edge, which is not internal - while (e != gp_GetLastArc(theGraph, vertex)) - { - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - e = gp_GetNextArc(theGraph, e); - } - - return OK; -} - -/******************************************************************** - _RestoreInternalEdges() - Reverses the effects of _HideInternalEdges() - ********************************************************************/ - -int _RestoreInternalEdges(graphP theGraph, int stackBottom) -{ - return _RestoreHiddenEdges(theGraph, stackBottom); -} - -/******************************************************************** - _RestoreHiddenEdges() - - Each entry on the stack, down to stackBottom, is assumed to be an - edge record (arc) pushed in concert with invoking gp_HideEdge(). - Each edge is restored using gp_RestoreEdge() in exact reverse of the - hiding order. The stack is reduced in size to stackBottom. - - Returns OK on success, NOTOK on internal failure. - ********************************************************************/ - -int _RestoreHiddenEdges(graphP theGraph, int stackBottom) -{ - int e; - - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, e); - if (gp_IsNotArc(e)) - return NOTOK; - gp_RestoreEdge(theGraph, e); - } - - return OK; -} - -/******************************************************************** - gp_HideVertex() - - Pushes onto the graph's stack and hides all arc nodes of the vertex. - Additional integers are then pushed so that the result is reversible - by gp_RestoreVertex(). See that method for details on the expected - stack segment. - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_HideVertex(graphP theGraph, int vertex) -{ - if (gp_IsNotVertex(vertex)) - return NOTOK; - - return theGraph->functions.fpHideVertex(theGraph, vertex); -} - -int _HideVertex(graphP theGraph, int vertex) -{ - int hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack); - int e = gp_GetFirstArc(theGraph, vertex); - - // Cycle through all the edges, pushing and hiding each - while (gp_IsArc(e)) - { - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - e = gp_GetNextArc(theGraph, e); - } - - // Push the additional integers needed by gp_RestoreVertex() - sp_Push(theGraph->theStack, hiddenEdgeStackBottom); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, NIL); - sp_Push(theGraph->theStack, vertex); - - return OK; -} - -/******************************************************************** - gp_ContractEdge() - - Contracts the edge e=(u,v). This hides the edge (both e and its - twin arc), and it also identifies vertex v with u. - See gp_IdentifyVertices() for further details. - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_ContractEdge(graphP theGraph, int e) -{ - if (gp_IsNotArc(e)) - return NOTOK; - - return theGraph->functions.fpContractEdge(theGraph, e); -} - -int _ContractEdge(graphP theGraph, int e) -{ - int eBefore, u, v; - - if (gp_IsNotArc(e)) - return NOTOK; - - u = gp_GetNeighbor(theGraph, gp_GetTwinArc(theGraph, e)); - v = gp_GetNeighbor(theGraph, e); - - eBefore = gp_GetNextArc(theGraph, e); - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - - return gp_IdentifyVertices(theGraph, u, v, eBefore); -} - -/******************************************************************** - gp_IdentifyVertices() - - Identifies vertex v with vertex u by transferring all adjacencies - of v to u. Any duplicate edges are removed as described below. - The non-duplicate edges of v are added to the adjacency list of u - without disturbing their relative order, and they are added before - the edge record eBefore in u's list. If eBefore is NIL, then the - edges are simply appended to u's list. - - If u and v are adjacent, then gp_HideEdge() is invoked to remove - the edge e=(u,v). Then, the edges of v that indicate neighbors of - u are also hidden. This is done by setting the visited flags of - u's neighbors, then traversing the adjacency list of v. For each - visited neighbor of v, the edge is hidden because it would duplicate - an adjacency already expressed in u's list. Finally, the remaining - edges of v are moved to u's list, and each twin arc is adjusted - to indicate u as a neighbor rather than v. - - This routine assumes that the visited flags are clear beforehand, - and visited flag settings made herein are cleared before returning. - - The following are pushed, in order, onto the graph's built-in stack: - 1) an integer for each hidden edge - 2) the stack size before any hidden edges were pushed - 3) six integers that indicate u, v and the edges moved from v to u - - An algorithm that identifies a series of vertices, either through - directly calling this method or via gp_ContractEdge(), can unwind - the identifications using gp_RestoreIdentifications(), which - invokes gp_RestoreVertex() repeatedly. - - Returns OK on success, NOTOK on internal failure - ********************************************************************/ - -int gp_IdentifyVertices(graphP theGraph, int u, int v, int eBefore) -{ - return theGraph->functions.fpIdentifyVertices(theGraph, u, v, eBefore); -} - -int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore) -{ - int e = gp_GetNeighborEdgeRecord(theGraph, u, v); - int hiddenEdgeStackBottom, eBeforePred; - - // If the vertices are adjacent, then the identification is - // essentially an edge contraction with a bit of fixup. - if (gp_IsArc(e)) - { - int result = gp_ContractEdge(theGraph, e); - - // The edge contraction operation pushes one hidden edge then - // recursively calls this method. This method then pushes K - // hidden edges then an integer indicating where the top of - // stack was before the edges were hidden. That integer - // indicator must be decremented, thereby incrementing the - // number of hidden edges to K+1. - // After pushing the K hidden edges and the stackBottom of - // the hidden edges, the recursive call to this method pushes - // six more integers to indicate edges that were moved from - // v to u, so the "hidden edges stackBottom" is in the next - // position down. - int hiddenEdgesStackBottomIndex = sp_GetCurrentSize(theGraph->theStack)-7; - int hiddenEdgesStackBottomValue = sp_Get(theGraph->theStack, hiddenEdgesStackBottomIndex); - - sp_Set(theGraph->theStack, hiddenEdgesStackBottomIndex, hiddenEdgesStackBottomValue - 1); - - return result; - } - - // Now, u and v are not adjacent. Before we do any edge hiding or - // moving, we record the current stack size, as this is the - // stackBottom for the edges that will be hidden next. - hiddenEdgeStackBottom = sp_GetCurrentSize(theGraph->theStack); - - // Mark as visited all neighbors of u - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - return NOTOK; - - gp_SetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); - e = gp_GetNextArc(theGraph, e); - } - - // For each edge record of v, if the neighbor is visited, then - // push and hide the edge. - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - if (gp_GetVertexVisited(theGraph, gp_GetNeighbor(theGraph, e))) - { - sp_Push(theGraph->theStack, e); - gp_HideEdge(theGraph, e); - } - e = gp_GetNextArc(theGraph, e); - } - - // Mark as unvisited all neighbors of u - e = gp_GetFirstArc(theGraph, u); - while (gp_IsArc(e)) - { - gp_ClearVertexVisited(theGraph, gp_GetNeighbor(theGraph, e)); - e = gp_GetNextArc(theGraph, e); - } - - // Push the hiddenEdgeStackBottom as a record of how many hidden - // edges were pushed (also, see above for Contract Edge adjustment) - sp_Push(theGraph->theStack, hiddenEdgeStackBottom); - - // Moving v's adjacency list to u is aided by knowing the predecessor - // of u's eBefore (the edge record in u's list before which the - // edge records of v will be added). - eBeforePred = gp_IsArc(eBefore) - ? gp_GetPrevArc(theGraph, eBefore) - : gp_GetLastArc(theGraph, u); - - // Turns out we only need to record six integers related to the edges - // being moved in order to easily restore them later. - sp_Push(theGraph->theStack, eBefore); - sp_Push(theGraph->theStack, gp_GetLastArc(theGraph, v)); - sp_Push(theGraph->theStack, gp_GetFirstArc(theGraph, v)); - sp_Push(theGraph->theStack, eBeforePred); - sp_Push(theGraph->theStack, u); - sp_Push(theGraph->theStack, v); - - // For the remaining edge records of v, reassign the 'v' member - // of each twin arc to indicate u rather than v. - e = gp_GetFirstArc(theGraph, v); - while (gp_IsArc(e)) - { - gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), u); - e = gp_GetNextArc(theGraph, e); - } - - // If v has any edges left after hiding edges, indicating common neighbors with u, ... - if (gp_IsArc(gp_GetFirstArc(theGraph, v))) - { - // Then perform the list union of v into u between eBeforePred and eBefore - if (gp_IsArc(eBeforePred)) - { - if (gp_IsArc(gp_GetFirstArc(theGraph, v))) - { - gp_SetNextArc(theGraph, eBeforePred, gp_GetFirstArc(theGraph, v)); - gp_SetPrevArc(theGraph, gp_GetFirstArc(theGraph, v), eBeforePred); - } - } - else - { - gp_SetFirstArc(theGraph, u, gp_GetFirstArc(theGraph, v)); - } - - if (gp_IsArc(eBefore)) - { - if (gp_IsArc(gp_GetLastArc(theGraph, v))) - { - gp_SetNextArc(theGraph, gp_GetLastArc(theGraph, v), eBefore); - gp_SetPrevArc(theGraph, eBefore, gp_GetLastArc(theGraph, v)); - } - } - else - { - gp_SetLastArc(theGraph, u, gp_GetLastArc(theGraph, v)); - } - - gp_SetFirstArc(theGraph, v, NIL); - gp_SetLastArc(theGraph, v, NIL); - } - - return OK; -} - -/******************************************************************** - gp_RestoreVertex() - - This method assumes the built-in graph stack contents are the result - of vertex hide, vertex identify and edge contract operations. - This content consists of segments of integers, each segment - corresponding to the removal of a vertex during an edge contraction - or vertex identification in which a vertex v was merged into a - vertex u. The segment contains two blocks of integers. - The first block contains information about u, v, the edge records - in v's adjacency list that were added to u, and where in u's - adjacency list they were added. The second block of integers - contains a list of edges incident to v that were hidden from the - graph because they were incident to neighbors of v that were also - neighbors of u (so they would have produced duplicate edges had - they been left in v's adjacency list when it was merged with u's - adjacency list). - - This method pops the first block of the segment off the stack and - uses the information to help remove v's adjacency list from u and - restore it into v. Then, the second block is removed from the - stack, and each indicated edge is restored from the hidden state. - - It is anticipated that this method will be overloaded by extension - algorithms to perform some processing as each vertex is restored. - Before restoration, the topmost segment has the following structure: - - ... FHE ... LHE HESB e_u_succ e_v_last e_v_first e_u_pred u v - ^------------| - - FHE = First hidden edge - LHE = Last hidden edge - HESB = Hidden edge stack bottom - e_u_succ, e_u_pred = The edges of u between which the edges of v - were inserted. NIL can appear if the edges of v - were added to the beginning or end of u's list - e_v_first, e_v_last = The first and last edges of v's list, once - the hidden edges were removed - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_RestoreVertex(graphP theGraph) -{ - return theGraph->functions.fpRestoreVertex(theGraph); -} - -int _RestoreVertex(graphP theGraph) -{ -int u, v, e_u_succ, e_u_pred, e_v_first, e_v_last, HESB, e; - - if (sp_GetCurrentSize(theGraph->theStack) < 7) - return NOTOK; - - sp_Pop(theGraph->theStack, v); - sp_Pop(theGraph->theStack, u); - sp_Pop(theGraph->theStack, e_u_pred); - sp_Pop(theGraph->theStack, e_v_first); - sp_Pop(theGraph->theStack, e_v_last); - sp_Pop(theGraph->theStack, e_u_succ); - - // If u is not NIL, then vertex v was identified with u. Otherwise, v was - // simply hidden, so we skip to restoring the hidden edges. - if (gp_IsVertex(u)) - { - // Remove v's adjacency list from u, including accounting for degree 0 case - if (gp_IsArc(e_u_pred)) - { - gp_SetNextArc(theGraph, e_u_pred, e_u_succ); - // If the successor edge exists, link it to the predecessor, - // otherwise the predecessor is the new last arc - if (gp_IsArc(e_u_succ)) - gp_SetPrevArc(theGraph, e_u_succ, e_u_pred); - else - gp_SetLastArc(theGraph, u, e_u_pred); - } - else if (gp_IsArc(e_u_succ)) - { - // The successor arc exists, but not the predecessor, - // so the successor is the new first arc - gp_SetPrevArc(theGraph, e_u_succ, NIL); - gp_SetFirstArc(theGraph, u, e_u_succ); - } - else - { - // Just in case u was degree zero - gp_SetFirstArc(theGraph, u, NIL); - gp_SetLastArc(theGraph, u, NIL); - } - - // Place v's adjacency list into v, including accounting for degree 0 case - gp_SetFirstArc(theGraph, v, e_v_first); - gp_SetLastArc(theGraph, v, e_v_last); - if (gp_IsArc(e_v_first)) - gp_SetPrevArc(theGraph, e_v_first, NIL); - if (gp_IsArc(e_v_last)) - gp_SetPrevArc(theGraph, e_v_last, NIL); - - // For each edge record restored to v's adjacency list, reassign the 'v' member - // of each twin arc to indicate v rather than u. - e = e_v_first; - while (gp_IsArc(e)) - { - gp_SetNeighbor(theGraph, gp_GetTwinArc(theGraph, e), v); - e = (e == e_v_last ? NIL : gp_GetNextArc(theGraph, e)); - } - } - - // Restore the hidden edges of v, if any - sp_Pop(theGraph->theStack, HESB); - return _RestoreHiddenEdges(theGraph, HESB); -} - -/******************************************************************** - gp_RestoreVertices() - - This method assumes the built-in graph stack has content consistent - with numerous vertex identification or edge contraction operations. - This method unwinds the stack, moving edges back to their original - vertex owners and restoring hidden edges. - This method is a simple iterator that invokes gp_RestoreVertex() - until the stack is empty, so extension algorithms are more likely - to overload gp_RestoreVertex(). - - Returns OK for success, NOTOK for internal failure. - ********************************************************************/ - -int gp_RestoreVertices(graphP theGraph) -{ - while (sp_NonEmpty(theGraph->theStack)) - { - if (gp_RestoreVertex(theGraph) != OK) - return NOTOK; - } - - return OK; -} - -/**************************************************************************** - _ComputeArcType() - This is just a little helper function that automates a sequence of decisions - that has to be made a number of times. - An edge record is being added to the adjacency list of a; it indicates that - b is a neighbor. The edgeType can be either 'tree' (EDGE_TYPE_PARENT or - EDGE_TYPE_CHILD) or 'cycle' (EDGE_TYPE_BACK or EDGE_TYPE_FORWARD). - If a or b is a root copy, we translate to the non-virtual counterpart, - then wedetermine which has the lesser DFI. If a has the lower DFI then the - edge record is a tree edge to a child (EDGE_TYPE_CHILD) if edgeType indicates - a tree edge. If edgeType indicates a cycle edge, then it is a forward cycle - edge (EDGE_TYPE_FORWARD) to a descendant. - Symmetric conditions define the types for a > b. - ****************************************************************************/ - -int _ComputeArcType(graphP theGraph, int a, int b, int edgeType) -{ - a = gp_IsVirtualVertex(theGraph, a) ? gp_GetPrimaryVertexFromRoot(theGraph, a) : a; - b = gp_IsVirtualVertex(theGraph, b) ? gp_GetPrimaryVertexFromRoot(theGraph, b) : b; - - if (a < b) - return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_CHILD : EDGE_TYPE_FORWARD; - - return edgeType == EDGE_TYPE_PARENT || edgeType == EDGE_TYPE_CHILD ? EDGE_TYPE_PARENT : EDGE_TYPE_BACK; -} - -/**************************************************************************** - _SetEdgeType() - When we are restoring an edge, we must restore its type (tree edge or cycle edge). - We can deduce what the type was based on other information in the graph. Each - arc of the edge gets the appropriate type setting (parent/child or back/forward). - This method runs in constant time plus the degree of vertex u, or constant - time if u is known to have a degree bound by a constant. - ****************************************************************************/ - -int _SetEdgeType(graphP theGraph, int u, int v) -{ -int e, eTwin, u_orig, v_orig; - - // If u or v is a virtual vertex (a root copy), then get the non-virtual counterpart. - u_orig = gp_IsVirtualVertex(theGraph, u) ? (gp_GetPrimaryVertexFromRoot(theGraph, u)) : u; - v_orig = gp_IsVirtualVertex(theGraph, v) ? (gp_GetPrimaryVertexFromRoot(theGraph, v)) : v; - - // Get the edge for which we will set the type - - e = gp_GetNeighborEdgeRecord(theGraph, u, v); - eTwin = gp_GetTwinArc(theGraph, e); - - // If u_orig is the parent of v_orig, or vice versa, then the edge is a tree edge - - if (gp_GetVertexParent(theGraph, v_orig) == u_orig || - gp_GetVertexParent(theGraph, u_orig) == v_orig) - { - if (u_orig > v_orig) - { - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_PARENT); - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_CHILD); - } - else - { - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_PARENT); - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_CHILD); - } - } - - // Otherwise it is a back edge - - else - { - if (u_orig > v_orig) - { - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_BACK); - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_FORWARD); - } - else - { - gp_ResetEdgeType(theGraph, eTwin, EDGE_TYPE_BACK); - gp_ResetEdgeType(theGraph, e, EDGE_TYPE_FORWARD); - } - } - - return OK; -} - -/******************************************************************** - _DeleteUnmarkedEdgesInBicomp() - - This function deletes from a given biconnected component all edges - whose visited member is zero. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _DeleteUnmarkedEdgesInBicomp(graphP theGraph, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetEdgeVisited(theGraph, e) ? gp_GetNextArc(theGraph, e) : gp_DeleteEdge(theGraph, e, 0); - } - } - return OK; -} - -/******************************************************************** - _ClearInvertedFlagsInBicomp() - - This function clears the inverted flag markers on any edges in a - given biconnected component. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - Returns OK on success, NOTOK on implementation failure - ********************************************************************/ - -int _ClearInvertedFlagsInBicomp(graphP theGraph, int BicompRoot) -{ -int V, e; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - { - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - gp_ClearEdgeFlagInverted(theGraph, e); - } - - e = gp_GetNextArc(theGraph, e); - } - } - return OK; -} - -/******************************************************************** - _GetBicompSize() - - Determine the number of vertices in the bicomp. - - The stack is used but preserved. In debug mode, NOTOK can result if - there is a stack overflow. This method pushes at most one integer - per vertex in the bicomp. - - Returns a positive number on success, NOTOK on implementation failure - ********************************************************************/ - -int _GetBicompSize(graphP theGraph, int BicompRoot) -{ -int V, e; -int theSize = 0; -int stackBottom = sp_GetCurrentSize(theGraph->theStack); - - sp_Push(theGraph->theStack, BicompRoot); - while (sp_GetCurrentSize(theGraph->theStack) > stackBottom) - { - sp_Pop(theGraph->theStack, V); - theSize++; - e = gp_GetFirstArc(theGraph, V); - while (gp_IsArc(e)) - { - if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD) - sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e)); - - e = gp_GetNextArc(theGraph, e); - } - } - return theSize; -} - -/******************************************************************** - debugNOTOK() - This function provides a non-void wrapper for exit(). - This is useful for debugging as it allows compilation of an exit - command in places where NOTOK is returned. - In exhaustive testing, we want to bail on the first NOTOK that occurs. - Comment out the exit() call to get a stack trace. - ********************************************************************/ - -int debugNOTOK() -{ - //exit(-1); - return 0; // NOTOK is normally defined to be zero -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.c deleted file mode 100644 index 6bc7ac764..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.c +++ /dev/null @@ -1,272 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#define _LISTCOLL_C - -#include "appconst.h" -#include "listcoll.h" -#include - -/***************************************************************************** - The data structure defined by this module manages a set of N objects - arranged as a collection of circular lists, each containing distinct - elements from the set. - - On construction, LCNew() creates an array of N nodes, each containing a - prev and next pointer. The identity of the node is given by its array index. - Each node's prev and next pointers are set to NIL, indicating that the node - is not currently part of a list. LCReset() can be called to reset all - pointers to NIL. - - The function LCFree() deallocates the collection of lists and clears the - pointer variable used to pass the collection. - - An empty list is indicated by NIL. To begin a list with node I, call - LCPrepend() or LCAppend() with the NIL list and with I as the node. The prev - and next pointers in node I are set to I and I is returned as the head of - the list. - - Future calls to LCPrepend() add a node J as the new first element of the list, - so the list given as input is pointed to by J's next, and J is returned as - the head of the list. - - Future calls to LCAppend() add a node J as the new last element, so the prev - pointer of the list given as input will indicate node J, and the input list - is returned as the head of the list. - - LCInsertAfter() adds a node immediately after a given anchor node. - - LCInsertBefore() adds a node immediately before a given anchor node and has - the same effect on a list as LCPrepend(). - - The function LCDelete() removes a node I from a list L. If node I is in the - list alone, then its pointers are set to NIL, and NIL is returned as the list. - If node I is not alone in the list, but it is the head of the list (in other - words, I is equal to L), then L's sucessor is returned as the new head of the - list. Whether or not I equals L, node I is deleted by joining its predecessor - and successor nodes. - - LCCopy() copies the contents of one collection to another if both are of - equal size. - - LCGetNext() is used for forward iteration through a list in the collection. - The expected iteration pattern is first to process the node one has, then call - LCGetNext() to get the next node, so if the result of LCGetNext() would be the - head of the list, then NIL is returned instead. This simplifies most - coding operations involving LCGetNext(). - - LCGetPrev() is used for backward iteration through a list in the collection. - The expected iteration pattern is that the last list element will be obtained - by an initial call to LCGetPrev() with theNode equal to NIL. This call - should appear outside of the iteration loop. The iteration loop then - proceeds while the current node is not NIL. The loop body processes the - current node, then LCGetPrev() is called with theNode equal to the current - node. LCGetPrev() returns NIL if theNode is equal to theList. Otherwise, - the predecessor of theNode is returned. - - *****************************************************************************/ - -/***************************************************************************** - LCNew() - *****************************************************************************/ - -listCollectionP LCNew(int N) -{ -listCollectionP theListColl = NULL; - - if (N <= 0) return theListColl; - - theListColl = (listCollectionP) malloc(sizeof(listCollectionRec)); - if (theListColl != NULL) - { - theListColl->List = (lcnode *) malloc(N*sizeof(lcnode)); - if (theListColl->List == NULL) - { - free(theListColl); - theListColl = NULL; - } - else - { - theListColl->N = N; - LCReset(theListColl); - } - } - return theListColl; -} - -/***************************************************************************** - LCFree() - *****************************************************************************/ - -void LCFree(listCollectionP *pListColl) -{ - if (pListColl==NULL || *pListColl==NULL) return; - - if ((*pListColl)->List != NULL) - free((*pListColl)->List); - - free(*pListColl); - *pListColl = NULL; -} - -/***************************************************************************** - LCInsertAfter() - *****************************************************************************/ - -void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode) -{ - listColl->List[theNewNode].prev = theAnchor; - listColl->List[theNewNode].next = listColl->List[theAnchor].next; - listColl->List[listColl->List[theAnchor].next].prev = theNewNode; - listColl->List[theAnchor].next = theNewNode; -} - -/***************************************************************************** - LCInsertBefore() - *****************************************************************************/ - -void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode) -{ - LCPrepend(listColl, theAnchor, theNewNode); -} - -#ifndef SPEED_MACROS - -/***************************************************************************** - LCReset() - *****************************************************************************/ - -void LCReset(listCollectionP listColl) -{ -int K; - - for (K=0; K < listColl->N; K++) - listColl->List[K].prev = listColl->List[K].next = NIL; -} - -/***************************************************************************** - LCCopy() - *****************************************************************************/ - -void LCCopy(listCollectionP dst, listCollectionP src) -{ -int K; - - if (dst==NULL || src==NULL || dst->N != src->N) return; - - for (K=0; K < dst->N; K++) - dst->List[K] = src->List[K]; - -} - -/***************************************************************************** - LCGetNext() - *****************************************************************************/ - -int LCGetNext(listCollectionP listColl, int theList, int theNode) -{ -int next; - - if (listColl==NULL || theList==NIL || theNode==NIL) return NIL; - next = listColl->List[theNode].next; - return next==theList ? NIL : next; -} - -/***************************************************************************** - LCGetPrev() - *****************************************************************************/ - -int LCGetPrev(listCollectionP listColl, int theList, int theNode) -{ - if (listColl==NULL || theList==NIL) return NIL; - if (theNode == NIL) return listColl->List[theList].prev; - if (theNode == theList) return NIL; - return listColl->List[theNode].prev; -} - -/***************************************************************************** - LCPrepend() - *****************************************************************************/ - -int LCPrepend(listCollectionP listColl, int theList, int theNode) -{ - /* If the append worked, then theNode is last, which in a circular - list is the direct predecessor of the list head node, so we - just back up one. For singletons, the result is unchanged. */ - - return listColl->List[LCAppend(listColl, theList, theNode)].prev; -} - -/***************************************************************************** - LCAppend() - *****************************************************************************/ - -int LCAppend(listCollectionP listColl, int theList, int theNode) -{ - /* If the given list is empty, then the given node becomes the - singleton list output */ - - if (theList == NIL) - { - listColl->List[theNode].prev = listColl->List[theNode].next = theNode; - theList = theNode; - } - - /* Otherwise, make theNode the predecessor of head node of theList, - which is where the last node goes in a circular list. */ - - else - { - int pred = listColl->List[theList].prev; - - listColl->List[theList].prev = theNode; - listColl->List[theNode].next = theList; - listColl->List[theNode].prev = pred; - listColl->List[pred].next = theNode; - } - - /* Return the list (only really important if it was NIL) */ - - return theList; -} - -/***************************************************************************** - LCDelete() - *****************************************************************************/ - -int LCDelete(listCollectionP listColl, int theList, int theNode) -{ - /* If the list is a singleton, then NIL its pointers and - return NIL for theList*/ - - if (listColl->List[theList].next == theList) - { - listColl->List[theList].prev = listColl->List[theList].next = NIL; - theList = NIL; - } - - /* Join predecessor and successor, dropping theNode from the list. - If theNode is the head of the list, then return the successor as - the new head node. */ - - else - { - int pred=listColl->List[theNode].prev, - succ=listColl->List[theNode].next; - - listColl->List[pred].next = succ; - listColl->List[succ].prev = pred; - - listColl->List[theNode].prev = listColl->List[theNode].next = NIL; - - if (theList == theNode) - theList = succ; - } - - return theList; -} - -#endif // SPEED_MACROS diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.h deleted file mode 100644 index de4b0f509..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/listcoll.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifndef _LISTCOLL_H -#define _LISTCOLL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* This include is needed for memset and memcpy */ -#include - -typedef struct -{ - int prev, next; -} lcnode; - -typedef struct -{ - int N; - lcnode *List; -} listCollectionRec; - -typedef listCollectionRec * listCollectionP; - -listCollectionP LCNew(int N); -void LCFree(listCollectionP *pListColl); - -void LCInsertAfter(listCollectionP listColl, int theAnchor, int theNewNode); -void LCInsertBefore(listCollectionP listColl, int theAnchor, int theNewNode); - -#ifndef SPEED_MACROS - -void LCReset(listCollectionP listColl); -void LCCopy(listCollectionP dst, listCollectionP src); - -int LCGetNext(listCollectionP listColl, int theList, int theNode); -int LCGetPrev(listCollectionP listColl, int theList, int theNode); - -int LCPrepend(listCollectionP listColl, int theList, int theNode); -int LCAppend(listCollectionP listColl, int theList, int theNode); -int LCDelete(listCollectionP listColl, int theList, int theNode); - -#else - -/* void LCReset(listCollectionP listColl); */ - -#define LCReset(listColl) memset(listColl->List, NIL_CHAR, listColl->N*sizeof(lcnode)) - -/* void LCCopy(listCollectionP dst, listCollectionP src) */ - -#define LCCopy(dst, src) memcpy(dst->List, src->List, src->N*sizeof(lcnode)) - -/* int LCGetNext(listCollectionP listColl, int theList, int theNode); - Return theNode's successor, unless it is theList head pointer */ - -#define LCGetNext(listColl, theList, theNode) listColl->List[theNode].next==theList ? NIL : listColl->List[theNode].next - -/* int LCGetPrev(listCollectionP listColl, int theList, int theNode); - Return theNode's predecessor unless theNode is theList head. - To start going backwards, use NIL for theNode, which returns theList head's predecessor - Usage: Obtain last node, loop while NIL not returned, process node then get predecessor. - After theList head processed, get predecessor returns NIL because we started with - theList head's predecessor. */ - -#define LCGetPrev(listColl, theList, theNode) \ - (theNode==NIL \ - ? listColl->List[theList].prev \ - : theNode==theList ? NIL : listColl->List[theNode].prev) - -/* int LCPrepend(listCollectionP listColl, int theList, int theNode); - If theList is empty, then theNode becomes its only member and is returned. - Otherwise, theNode is placed before theList head, and theNode is returned as the new head. */ - -#define LCPrepend(listColl, theList, theNode) \ - (theList==NIL \ - ? (listColl->List[theNode].prev = listColl->List[theNode].next = theNode) \ - : (listColl->List[theNode].next = theList, \ - listColl->List[theNode].prev = listColl->List[theList].prev, \ - listColl->List[listColl->List[theNode].prev].next = theNode, \ - listColl->List[theList].prev = theNode, \ - listColl->List[theList].prev)) - -/* int LCAppend(listCollectionP listColl, int theList, int theNode); - If theList is empty, then theNode becomes its only member and is returned. - Otherwise, theNode is placed before theList head, and then theList head is returned. */ - -#define LCAppend(listColl, theList, theNode) \ - (theList==NIL \ - ? (listColl->List[theNode].prev = listColl->List[theNode].next = theNode) \ - : (listColl->List[theNode].next = theList, \ - listColl->List[theNode].prev = listColl->List[theList].prev, \ - listColl->List[listColl->List[theNode].prev].next = theNode, \ - listColl->List[theList].prev = theNode, \ - theList)) - -/* int LCDelete(listCollectionP listColl, int theList, int theNode); - If theList contains only one node, then NIL it out and return NIL meaning empty list - Otherwise, join the predecessor and successor, then - return either the list head or its successor if the deleted node is the list head - (in that case, the caller makes the successor become the new list head).*/ - - -#define LCDelete(listColl, theList, theNode) \ - listColl->List[theList].next == theList \ - ? (listColl->List[theList].prev = listColl->List[theList].next = NIL) \ - : (listColl->List[listColl->List[theNode].prev].next = listColl->List[theNode].next, \ - listColl->List[listColl->List[theNode].next].prev = listColl->List[theNode].prev, \ - (theList==theNode ? listColl->List[theNode].next : theList)) - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.c deleted file mode 100644 index 4f3458931..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.c +++ /dev/null @@ -1,242 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "planarity.h" - -void ProjectTitle() -{ - // This message is the main location of the version number. - // The format is major.minor.maintenance.tweak - // Major is for an overhaul (e.g. many features, data structure change, change of backward compatibility) - // Minor is for a feature addition (e.g. a new algorithm implementation added, new interface) - // Maintenance is for functional revision (e.g. bug fix to existing algorithm implementation) - // Tweak is for a non-functional revision (e.g. change of build scripts or testing code, user-facing string changes) - - // If the version here is increased, also increase it in configure.ac - // Furthermore, a change of Major, Minor or Maintenance here should cause a change - // of Current, Revision and/or Age as documented in configure.ac - - Message("\n==================================================" - "\nThe Edge Addition Planarity Suite version 3.0.1.0" - "\nCopyright (c) 1997-2020 by John M. Boyer" - "\nContact info: jboyer at acm.org" - "\n==================================================" - "\n"); -} - -/**************************************************************************** - MAIN - ****************************************************************************/ - -int main(int argc, char *argv[]) -{ - int retVal=0; - - if (argc <= 1) - retVal = menu(); - - else if (argv[1][0] == '-') - retVal = commandLine(argc, argv); - - else - retVal = legacyCommandLine(argc, argv); - - // Close the log file if logging - gp_Log(NULL); - - return retVal; -} - -/**************************************************************************** - helpMessage() - ****************************************************************************/ - -int helpMessage(char *param) -{ - char *commandStr = - "C = command (algorithm implementation to run)\n" - " -p = Planar embedding and Kuratowski subgraph isolation\n" - " -d = Planar graph drawing by visibility representation\n" - " -o = Outerplanar embedding and obstruction isolation\n" - " -2 = Search for subgraph homeomorphic to K_{2,3}\n" - " -3 = Search for subgraph homeomorphic to K_{3,3}\n" - " -4 = Search for subgraph homeomorphic to K_4\n" - " -a = All of the above\n" - "\n"; - - ProjectTitle(); - - if (param == NULL) - { - Message( - "'planarity': if no command-line, then menu-driven\n" - "'planarity (-h|-help)': this message\n" - "'planarity (-h|-help) -menu': more help with menu-based command line\n" - "'planarity (-i|-info): copyright and license information\n" - "'planarity -test [-q] [C]': runs tests (optional quiet mode, single test)\n" - "\n" - ); - - Message( - "Common usages\n" - "-------------\n" - "planarity -s -q -p infile.txt embedding.out [obstruction.out]\n" - "Process infile.txt in quiet mode (-q), putting planar embedding in \n" - "embedding.out or (optionally) a Kuratowski subgraph in Obstruction.out\n" - "Process returns 0=planar, 1=nonplanar, -1=error\n" - "\n" - "planarity -s -q -d infile.txt embedding.out [drawing.out]\n" - "If graph in infile.txt is planar, then put embedding in embedding.out \n" - "and (optionally) an ASCII art drawing in drawing.out\n" - "Process returns 0=planar, 1=nonplanar, -1=error\n" - ); - } - - else if (strcmp(param, "-i") == 0 || strcmp(param, "-info") == 0) - { - Message( - "The Edge Addition Planarity Suite version 3.0.1.0\n" - "Copyright (c) 1997-2020, John M. Boyer\n" - "All rights reserved. \n" - "See the LICENSE.TXT file for licensing information. \n" - "\n" - "Includes a reference implementation of the following:\n" - "\n" - "* John M. Boyer. \"Subgraph Homeomorphism via the Edge Addition Planarity \n" - " Algorithm\". Journal of Graph Algorithms and Applications, Vol. 16, \n" - " no. 2, pp. 381-410, 2012. http://dx.doi.org/10.7155/jgaa.00268\n" - "\n" - "* John M. Boyer. \"A New Method for Efficiently Generating Planar Graph\n" - " Visibility Representations\". In P. Eades and P. Healy, editors,\n" - " Proceedings of the 13th International Conference on Graph Drawing 2005,\n" - " Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.\n" - " http://dx.doi.org/10.1007/11618058_47\n" - "\n" - "* John M. Boyer and Wendy J. Myrvold. \"On the Cutting Edge: Simplified O(n)\n" - " Planarity by Edge Addition\". Journal of Graph Algorithms and Applications,\n" - " Vol. 8, No. 3, pp. 241-273, 2004. http://dx.doi.org/10.7155/jgaa.00091\n" - "\n" - "* John M. Boyer. \"Simplified O(n) Algorithms for Planar Graph Embedding,\n" - " Kuratowski Subgraph Isolation, and Related Problems\". Ph.D. Dissertation,\n" - " University of Victoria, 2001. https://dspace.library.uvic.ca/handle/1828/9918\n" - "\n" - ); - } - - else if (strcmp(param, "-menu") == 0) - { - Message( - "'planarity -r [-q] C K N': Random graphs\n" - "'planarity -s [-q] C I O [O2]': Specific graph\n" - "'planarity -rm [-q] N O [O2]': Maximal planar random graph\n" - "'planarity -rn [-q] N O [O2]': Nonplanar random graph (maximal planar + edge)\n" - "'planarity I O [-n O2]': Legacy command-line (default -s -p)\n" - "\n" - ); - - Message("-q is for quiet mode (no messages to stdout and stderr)\n\n"); - - Message(commandStr); - - Message( - "K = # of graphs to randomly generate\n" - "N = # of vertices in each randomly generated graph\n" - "I = Input file (for work on a specific graph)\n" - "O = Primary output file\n" - " For example, if C=-p then O receives the planar embedding\n" - " If C=-3, then O receives a subgraph containing a K_{3,3}\n" - "O2= Secondary output file\n" - " For -s, if C=-p or -o, then O2 receives the embedding obstruction\n" - " For -s, if C=-d, then O2 receives a drawing of the planar graph\n" - " For -m and -n, O2 contains the original randomly generated graph\n" - "\n" - ); - - Message( - "planarity process results: 0=OK, -1=NOTOK, 1=NONEMBEDDABLE\n" - " 1 result only produced by specific graph mode (-s)\n" - " with command -2,-3,-4: found K_{2,3}, K_{3,3} or K_4\n" - " with command -p,-d: found planarity obstruction\n" - " with command -o: found outerplanarity obstruction\n" - ); - } - - FlushConsole(stdout); - return 0; -} - -/**************************************************************************** - MENU-DRIVEN PROGRAM - ****************************************************************************/ - -int menu() -{ -char Choice; - - do { - ProjectTitle(); - - Message("\n" - "P. Planar embedding and Kuratowski subgraph isolation\n" - "D. Planar graph drawing by visibility representation\n" - "O. Outerplanar embedding and obstruction isolation\n" - "2. Search for subgraph homeomorphic to K_{2,3}\n" - "3. Search for subgraph homeomorphic to K_{3,3}\n" - "4. Search for subgraph homeomorphic to K_4\n" - "H. Help message for command line version\n" - "R. Reconfigure options\n" - "X. Exit\n" - "\n" - ); - - Prompt("Enter Choice: "); - fflush(stdin); - scanf(" %c", &Choice); - Choice = tolower(Choice); - - if (Choice == 'h') - helpMessage(NULL); - - else if (Choice == 'r') - Reconfigure(); - - else if (Choice != 'x') - { - char *secondOutfile = NULL; - if (Choice == 'p' || Choice == 'd' || Choice == 'o') - secondOutfile =""; - - if (!strchr("pdo234", Choice)) { - Message("Invalid menu choice, please try again."); - } else { - switch (tolower(Mode)) - { - case 's' : SpecificGraph(Choice, NULL, NULL, secondOutfile); break; - case 'r' : RandomGraphs(Choice, 0, 0); break; - case 'm' : RandomGraph(Choice, 0, 0, NULL, NULL); break; - case 'n' : RandomGraph(Choice, 1, 0, NULL, NULL); break; - } - } - } - - if (Choice != 'r' && Choice != 'x') - { - Prompt("\nPress a key then hit ENTER to continue..."); - fflush(stdin); - scanf(" %*c"); - fflush(stdin); - Message("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - FlushConsole(stdout); - } - - } while (Choice != 'x'); - - // Certain debuggers don't terminate correctly with pending output content - FlushConsole(stdout); - FlushConsole(stderr); - - return 0; -} diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.h deleted file mode 100644 index 4b305d296..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarity.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef PLANARITY_H -#define PLANARITY_H - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#endif -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include "graph.h" -#include "platformTime.h" - -#include "graphK23Search.h" -#include "graphK33Search.h" -#include "graphK4Search.h" -#include "graphDrawPlanar.h" - -void ProjectTitle(); -int helpMessage(char *param); - -/* Functions that call the Graph Library */ -int SpecificGraph(char command, char *infileName, char *outfileName, char *outfile2Name); -int RandomGraph(char command, int extraEdges, int numVertices, char *outfileName, char *outfile2Name); -int RandomGraphs(char command, int, int); - -int makeg_main(char command, int argc, char *argv[]); - -/* Command line, Menu, and Configuration */ -int commandLine(int argc, char *argv[]); -int legacyCommandLine(int argc, char *argv[]); -int menu(); - -extern char Mode, - OrigOut, - EmbeddableOut, - ObstructedOut, - AdjListsForEmbeddingsOut, - quietMode; - -void Reconfigure(); - -/* Low-level Utilities */ -#define MAXLINE 1024 -extern char Line[MAXLINE]; - -void Message(char *message); -void ErrorMessage(char *message); -void FlushConsole(FILE *f); -void Prompt(char *message); - -void SaveAsciiGraph(graphP theGraph, char *filename); - -int TextFilesEqual(char *file1Name, char *file2Name); - -int GetEmbedFlags(char command); -char *GetAlgorithmName(char command); -void AttachAlgorithm(graphP theGraph, char command); - -char *ConstructInputFilename(char *infileName); -char *ConstructPrimaryOutputFilename(char *infileName, char *outfileName, char command); -void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityCommandLine.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityCommandLine.c deleted file mode 100644 index f8e6068fe..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityCommandLine.c +++ /dev/null @@ -1,396 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "planarity.h" - -#include - -int runQuickRegressionTests(int argc, char *argv[]); -int callRandomGraphs(int argc, char *argv[]); -int callSpecificGraph(int argc, char *argv[]); -int callRandomMaxPlanarGraph(int argc, char *argv[]); -int callRandomNonplanarGraph(int argc, char *argv[]); - -/**************************************************************************** - Command Line Processor - ****************************************************************************/ - -int commandLine(int argc, char *argv[]) -{ - int Result = OK; - - if (argc >= 3 && strcmp(argv[2], "-q") == 0) - quietMode = 'y'; - - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0) - { - Result = helpMessage(argc >= 3 ? argv[2] : NULL); - } - - else if (strcmp(argv[1], "-i") == 0 || strcmp(argv[1], "-info") == 0) - { - Result = helpMessage(argv[1]); - } - - else if (strcmp(argv[1], "-test") == 0) - Result = runQuickRegressionTests(argc, argv); - - else if (strcmp(argv[1], "-r") == 0) - Result = callRandomGraphs(argc, argv); - - else if (strcmp(argv[1], "-s") == 0) - Result = callSpecificGraph(argc, argv); - - else if (strcmp(argv[1], "-rm") == 0) - Result = callRandomMaxPlanarGraph(argc, argv); - - else if (strcmp(argv[1], "-rn") == 0) - Result = callRandomNonplanarGraph(argc, argv); - - else - { - ErrorMessage("Unsupported command line. Here is the help for this program.\n"); - helpMessage(NULL); - Result = NOTOK; - } - - return Result == OK ? 0 : (Result == NONEMBEDDABLE ? 1 : -1); -} - -/**************************************************************************** - Legacy Command Line Processor from version 1.x - ****************************************************************************/ - -int legacyCommandLine(int argc, char *argv[]) -{ -graphP theGraph = gp_New(); -int Result; - - Result = gp_Read(theGraph, argv[1]); - if (Result != OK) - { - if (Result != NONEMBEDDABLE) - { - if (strlen(argv[1]) > MAXLINE - 100) - sprintf(Line, "Failed to read graph\n"); - else - sprintf(Line, "Failed to read graph %s\n", argv[1]); - ErrorMessage(Line); - return -2; - } - } - - Result = gp_Embed(theGraph, EMBEDFLAGS_PLANAR); - - if (Result == OK) - { - gp_SortVertices(theGraph); - gp_Write(theGraph, argv[2], WRITE_ADJLIST); - } - - else if (Result == NONEMBEDDABLE) - { - if (argc >= 5 && strcmp(argv[3], "-n")==0) - { - gp_SortVertices(theGraph); - gp_Write(theGraph, argv[4], WRITE_ADJLIST); - } - } - else - Result = NOTOK; - - gp_Free(&theGraph); - - // In the legacy 1.x versions, OK/NONEMBEDDABLE was 0 and NOTOK was -2 - return Result==OK || Result==NONEMBEDDABLE ? 0 : -2; -} - -/**************************************************************************** - Quick regression test - ****************************************************************************/ - -int runSpecificGraphTests(); -int runSpecificGraphTest(char *command, char *infileName); - -int runQuickRegressionTests(int argc, char *argv[]) -{ - if (runSpecificGraphTests() < 0) - return -1; - - return 0; -} - -int runSpecificGraphTests() -{ - char origDir[2049]; - int retVal = 0; - - if (!getcwd(origDir, 2048)) - return -1; - - if (chdir("samples") != 0) - { - if (chdir("..") != 0 || chdir("samples") != 0) - { - // Warn but give success result - printf("WARNING: Unable to change to samples directory to run tests on samples.\n"); - return 0; - } - } - -#if NIL == 0 - if (runSpecificGraphTest("-p", "maxPlanar5.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "maxPlanar5.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "drawExample.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-p", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-o", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-2", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-3", "Petersen.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-4", "Petersen.txt") < 0) - retVal = -1; - -#endif - - if (runSpecificGraphTest("-p", "maxPlanar5.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "maxPlanar5.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-d", "drawExample.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-p", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-o", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-2", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-3", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (runSpecificGraphTest("-4", "Petersen.0-based.txt") < 0) - retVal = -1; - - if (retVal == 0) - printf("Tests of all specific graphs succeeded\n"); - - chdir(origDir); - FlushConsole(stdout); - return retVal; -} - -int runSpecificGraphTest(char *command, char *infileName) -{ - char *commandLine[] = { - "planarity", "-s", "C", "infile", "outfile", "outfile2" - }; - char *outfileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]); - char *outfile2Name = ""; - char *testfileName = strdup(outfileName); - int Result = 0; - - if (testfileName == NULL) - return -1; - - outfileName = strdup(strcat(outfileName, ".test.txt")); - if (outfileName == NULL) - { - free(testfileName); - return -1; - } - - // 'planarity -s [-q] C I O [O2]': Specific graph - commandLine[2] = command; - commandLine[3] = infileName; - commandLine[4] = outfileName; - commandLine[5] = outfile2Name; - - Result = callSpecificGraph(6, commandLine); - if (Result == OK || Result == NONEMBEDDABLE) - Result = 0; - else - { - ErrorMessage("Test failed (graph processor returned failure result).\n"); - Result = -1; - } - - if (Result == 0) - { - if (TextFilesEqual(testfileName, outfileName) == TRUE) - { - Message("Test succeeded (result equal to exemplar).\n"); - unlink(outfileName); - } - else - { - ErrorMessage("Test failed (result not equal to exemplar).\n"); - Result = -1; - } - } - - // For graph drawing, secondary file is outfileName + ".render.txt" - - if (command[1] == 'd' && Result == 0) - { - outfile2Name = ConstructPrimaryOutputFilename(NULL, outfileName, command[1]); - free(outfileName); - outfileName = strdup(strcat(outfile2Name, ".render.txt")); - - free(testfileName); - testfileName = ConstructPrimaryOutputFilename(infileName, NULL, command[1]); - testfileName = strdup(strcat(testfileName, ".render.txt")); - - if (Result == 0) - { - if (TextFilesEqual(testfileName, outfileName) == TRUE) - { - Message("Test succeeded (secondary result equal to exemplar).\n"); - unlink(outfileName); - } - else - { - ErrorMessage("Test failed (secondary result not equal to exemplar).\n"); - Result = -1; - } - } - } - - Message("\n"); - - free(outfileName); - free(testfileName); - return Result; -} - -/**************************************************************************** - callRandomGraphs() - ****************************************************************************/ - -// 'planarity -r [-q] C K N': Random graphs -int callRandomGraphs(int argc, char *argv[]) -{ - char Choice = 0; - int offset = 0, NumGraphs, SizeOfGraphs; - - if (argc < 5) - return -1; - - if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q') - { - Choice = argv[3][1]; - if (argc < 6) - return -1; - offset = 1; - } - - NumGraphs = atoi(argv[3+offset]); - SizeOfGraphs = atoi(argv[4+offset]); - - return RandomGraphs(Choice, NumGraphs, SizeOfGraphs); -} - -/**************************************************************************** - callSpecificGraph() - ****************************************************************************/ - -// 'planarity -s [-q] C I O [O2]': Specific graph -int callSpecificGraph(int argc, char *argv[]) -{ - char Choice=0, *infileName=NULL, *outfileName=NULL, *outfile2Name=NULL; - int offset = 0; - - if (argc < 5) - return -1; - - if (argv[2][0] == '-' && (Choice = argv[2][1]) == 'q') - { - Choice = argv[3][1]; - if (argc < 6) - return -1; - offset = 1; - } - - infileName = argv[3+offset]; - outfileName = argv[4+offset]; - if (argc == 6+offset) - outfile2Name = argv[5+offset]; - - return SpecificGraph(Choice, infileName, outfileName, outfile2Name); -} - -/**************************************************************************** - callRandomMaxPlanarGraph() - ****************************************************************************/ - -// 'planarity -rm [-q] N O [O2]': Maximal planar random graph -int callRandomMaxPlanarGraph(int argc, char *argv[]) -{ - int offset = 0, numVertices; - char *outfileName = NULL, *outfile2Name = NULL; - - if (argc < 4) - return -1; - - if (argv[2][0] == '-' && argv[2][1] == 'q') - { - if (argc < 5) - return -1; - offset = 1; - } - - numVertices = atoi(argv[2+offset]); - outfileName = argv[3+offset]; - if (argc == 5+offset) - outfile2Name = argv[4+offset]; - - return RandomGraph('p', 0, numVertices, outfileName, outfile2Name); -} - -/**************************************************************************** - callRandomNonplanarGraph() - ****************************************************************************/ - -// 'planarity -rn [-q] N O [O2]': Non-planar random graph (maximal planar plus edge) -int callRandomNonplanarGraph(int argc, char *argv[]) -{ - int offset = 0, numVertices; - char *outfileName = NULL, *outfile2Name = NULL; - - if (argc < 4) - return -1; - - if (argv[2][0] == '-' && argv[2][1] == 'q') - { - if (argc < 5) - return -1; - offset = 1; - } - - numVertices = atoi(argv[2+offset]); - outfileName = argv[3+offset]; - if (argc == 5+offset) - outfile2Name = argv[4+offset]; - - return RandomGraph('p', 1, numVertices, outfileName, outfile2Name); -} diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityRandomGraphs.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityRandomGraphs.c deleted file mode 100644 index fb96b59a4..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityRandomGraphs.c +++ /dev/null @@ -1,500 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#pragma clang diagnostic ignored \ - "-Wincompatible-pointer-types-discards-qualifiers" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-result" -#endif -#include "planarity.h" - -void GetNumberIfZero(int * pNum, char * prompt, int min, int max); -void ReinitializeGraph(graphP * pGraph, int ReuseGraphs, char command); -graphP MakeGraph(int Size, char command); - -/**************************************************************************** - RandomGraphs() - Top-level method to randomly generate graphs to test the algorithm given by - the command parameter. - The number of graphs to generate, and the number of vertices for each graph, - can be sent as the second and third params. For each that is sent as zero, - this method will prompt the user for a value. - ****************************************************************************/ - -#define NUM_MINORS 9 - -int RandomGraphs(char command, int NumGraphs, int SizeOfGraphs) -{ - char theFileName[256]; - int K, countUpdateFreq; - int Result = OK, MainStatistic = 0; - int ObstructionMinorFreqs[NUM_MINORS]; - graphP theGraph = NULL, origGraph = NULL; - platform_time start, end; - int embedFlags = GetEmbedFlags(command); - int ReuseGraphs = TRUE; - int writeResult; - int writeErrorReported_Random = FALSE, - writeErrorReported_Embedded = FALSE, - writeErrorReported_AdjList = FALSE, - writeErrorReported_Obstructed = FALSE, - writeErrorReported_Error = FALSE; - - GetNumberIfZero(&NumGraphs, "Enter number of graphs to generate:", 1, - 1000000000); - GetNumberIfZero(&SizeOfGraphs, "Enter size of graphs:", 1, 10000); - - theGraph = MakeGraph(SizeOfGraphs, command); - origGraph = MakeGraph(SizeOfGraphs, command); - if (theGraph == NULL || origGraph == NULL) { - gp_Free(&theGraph); - return NOTOK; - } - - // Initialize a secondary statistics array - for (K = 0; K < NUM_MINORS; K++) - ObstructionMinorFreqs[K] = 0; - - // Seed the random number generator with "now". Do it after any prompting - // to tie randomness to human process of answering the prompt. - srand(time(NULL)); - - // Select a counter update frequency that updates more frequently with - // larger graphs and which is relatively prime with 10 so that all digits - // of the count will change even though we aren't showing the count value - // on every iteration - countUpdateFreq = 3579 / SizeOfGraphs; - countUpdateFreq = countUpdateFreq < 1 ? 1 : countUpdateFreq; - countUpdateFreq = - countUpdateFreq % 2 == 0 ? countUpdateFreq + 1 : countUpdateFreq; - countUpdateFreq = - countUpdateFreq % 5 == 0 ? countUpdateFreq + 2 : countUpdateFreq; - - // Start the count - fprintf(stdout, "0\r"); - fflush(stdout); - - // Start the timer - platform_GetTime(start); - - // Generate and process the number of graphs requested - for (K = 0; K < NumGraphs; K++) { - if ((Result = gp_CreateRandomGraph(theGraph)) == OK) { - if (tolower(OrigOut) == 'y') { - sprintf(theFileName, "random%c%d.txt", FILE_DELIMITER, - K % 10); - writeResult = gp_Write(theGraph, theFileName, WRITE_ADJLIST); - if (writeResult != OK && !writeErrorReported_Random) { - sprintf(Line, - "Failed to write graph %s\nMake the directory if " - "not present\n", - theFileName); - ErrorMessage(Line); - writeErrorReported_Random = TRUE; - } - } - - gp_CopyGraph(origGraph, theGraph); - - if (strchr("pdo234", command)) { - Result = gp_Embed(theGraph, embedFlags); - - if (gp_TestEmbedResultIntegrity(theGraph, origGraph, - Result) != Result) - Result = NOTOK; - - if (Result == OK) { - MainStatistic++; - - if (tolower(EmbeddableOut) == 'y') { - sprintf(theFileName, "embedded%c%d.txt", - FILE_DELIMITER, K % 10); - writeResult = - gp_Write(theGraph, theFileName, WRITE_ADJMATRIX); - if (writeResult != OK && - !writeErrorReported_Embedded) { - sprintf(Line, - "Failed to write graph %s\nMake the " - "directory if not present\n", - theFileName); - ErrorMessage(Line); - writeErrorReported_Embedded = TRUE; - } - } - - if (tolower(AdjListsForEmbeddingsOut) == 'y') { - sprintf(theFileName, "adjlist%c%d.txt", - FILE_DELIMITER, K % 10); - writeResult = - gp_Write(theGraph, theFileName, WRITE_ADJLIST); - if (writeResult != OK && - !writeErrorReported_AdjList) { - sprintf(Line, - "Failed to write graph %s\nMake the " - "directory if not present\n", - theFileName); - ErrorMessage(Line); - writeErrorReported_AdjList = TRUE; - } - } - } - else if (Result == NONEMBEDDABLE) { - if (embedFlags == EMBEDFLAGS_PLANAR || - embedFlags == EMBEDFLAGS_OUTERPLANAR) { - if (theGraph->IC.minorType & MINORTYPE_A) - ObstructionMinorFreqs[0]++; - else if (theGraph->IC.minorType & MINORTYPE_B) - ObstructionMinorFreqs[1]++; - else if (theGraph->IC.minorType & MINORTYPE_C) - ObstructionMinorFreqs[2]++; - else if (theGraph->IC.minorType & MINORTYPE_D) - ObstructionMinorFreqs[3]++; - else if (theGraph->IC.minorType & MINORTYPE_E) - ObstructionMinorFreqs[4]++; - - if (theGraph->IC.minorType & MINORTYPE_E1) - ObstructionMinorFreqs[5]++; - else if (theGraph->IC.minorType & MINORTYPE_E2) - ObstructionMinorFreqs[6]++; - else if (theGraph->IC.minorType & MINORTYPE_E3) - ObstructionMinorFreqs[7]++; - else if (theGraph->IC.minorType & MINORTYPE_E4) - ObstructionMinorFreqs[8]++; - - if (tolower(ObstructedOut) == 'y') { - sprintf(theFileName, "obstructed%c%d.txt", - FILE_DELIMITER, K % 10); - writeResult = gp_Write(theGraph, theFileName, - WRITE_ADJMATRIX); - if (writeResult != OK && - !writeErrorReported_Obstructed) { - sprintf(Line, - "Failed to write graph %s\nMake the " - "directory if not present\n", - theFileName); - ErrorMessage(Line); - writeErrorReported_Obstructed = TRUE; - } - } - } - } - } - - // If there is an error in processing, then write the file for - // debugging - if (Result != OK && Result != NONEMBEDDABLE) { - sprintf(theFileName, "error%c%d.txt", FILE_DELIMITER, K % 10); - writeResult = gp_Write(origGraph, theFileName, WRITE_ADJLIST); - if (writeResult != OK && !writeErrorReported_Error) { - sprintf(Line, - "Failed to write graph %s\nMake the directory if " - "not present\n", - theFileName); - ErrorMessage(Line); - writeErrorReported_Error = TRUE; - } - } - } - - // Reinitialize or recreate graphs for next iteration - ReinitializeGraph(&theGraph, ReuseGraphs, command); - ReinitializeGraph(&origGraph, ReuseGraphs, command); - - // Show progress, but not so often that it bogs down progress - if (quietMode == 'n' && (K + 1) % countUpdateFreq == 0) { - fprintf(stdout, "%d\r", K + 1); - fflush(stdout); - } - - // Terminate loop on error - if (Result != OK && Result != NONEMBEDDABLE) { - ErrorMessage("\nError found\n"); - Result = NOTOK; - break; - } - } - - // Stop the timer - platform_GetTime(end); - - // Finish the count - fprintf(stdout, "%d\n", NumGraphs); - fflush(stdout); - - // Free the graph structures created before the loop - gp_Free(&theGraph); - gp_Free(&origGraph); - - // Print some demographic results - if (Result == OK || Result == NONEMBEDDABLE) - Message("\nNo Errors Found."); - sprintf(Line, "\nDone (%.3lf seconds).\n", - platform_GetDuration(start, end)); - Message(Line); - - // Report statistics for planar or outerplanar embedding - if (embedFlags == EMBEDFLAGS_PLANAR || - embedFlags == EMBEDFLAGS_OUTERPLANAR) { - sprintf(Line, "Num Embedded=%d.\n", MainStatistic); - Message(Line); - - for (K = 0; K < 5; K++) { - // Outerplanarity does not produces minors C and D - if (embedFlags == EMBEDFLAGS_OUTERPLANAR && (K == 2 || K == 3)) - continue; - - sprintf(Line, "Minor %c = %d\n", K + 'A', - ObstructionMinorFreqs[K]); - Message(Line); - } - - if (!(embedFlags & ~EMBEDFLAGS_PLANAR)) { - sprintf(Line, "\nNote: E1 are added to C, E2 are added to A, and " - "E=E3+E4+K5 homeomorphs.\n"); - Message(Line); - - for (K = 5; K < NUM_MINORS; K++) { - sprintf(Line, "Minor E%d = %d\n", K - 4, - ObstructionMinorFreqs[K]); - Message(Line); - } - } - } - - // Report statistics for graph drawing - else if (embedFlags == EMBEDFLAGS_DRAWPLANAR) { - sprintf(Line, "Num Graphs Embedded and Drawn=%d.\n", MainStatistic); - Message(Line); - } - - // Report statistics for subgraph homeomorphism algorithms - else if (embedFlags == EMBEDFLAGS_SEARCHFORK23) { - sprintf(Line, - "Of the generated graphs, %d did not contain a K_{2,3} " - "homeomorph as a subgraph.\n", - MainStatistic); - Message(Line); - } - else if (embedFlags == EMBEDFLAGS_SEARCHFORK33) { - sprintf(Line, - "Of the generated graphs, %d did not contain a K_{3,3} " - "homeomorph as a subgraph.\n", - MainStatistic); - Message(Line); - } - else if (embedFlags == EMBEDFLAGS_SEARCHFORK4) { - sprintf(Line, - "Of the generated graphs, %d did not contain a K_4 " - "homeomorph as a subgraph.\n", - MainStatistic); - Message(Line); - } - - - FlushConsole(stdout); - - return Result == OK || Result == NONEMBEDDABLE ? OK : NOTOK; -} - -/**************************************************************************** - GetNumberIfZero() - Internal function that gets a number if the given *pNum is zero. - The prompt is displayed if the number must be obtained from the user. - Whether the given number is used or obtained from the user, the function - ensures it is in the range [min, max] and assigns the midpoint value if - it is not. - ****************************************************************************/ - -void GetNumberIfZero(int * pNum, char * prompt, int min, int max) -{ - if (*pNum == 0) { - Prompt(prompt); - scanf(" %d", pNum); - } - - if (min < 1) - min = 1; - if (max < min) - max = min; - - if (*pNum < min || *pNum > max) { - *pNum = (max + min) / 2; - sprintf(Line, "Number out of range [%d, %d]; changed to %d\n", min, - max, *pNum); - ErrorMessage(Line); - } -} - -/**************************************************************************** - MakeGraph() - Internal function that makes a new graph, initializes it, and attaches an - algorithm to it based on the command. - ****************************************************************************/ - -graphP MakeGraph(int Size, char command) -{ - graphP theGraph; - if ((theGraph = gp_New()) == NULL || gp_InitGraph(theGraph, Size) != OK) { - ErrorMessage("Error creating space for a graph of the given size.\n"); - gp_Free(&theGraph); - return NULL; - } - - // Enable the appropriate feature. Although the same code appears in - // SpecificGraph, it is deliberately not separated to a common utility - // because SpecificGraph is used as a self-contained tutorial. It is not - // that hard to update both locations when new algorithms are added. - - switch (command) { - case 'd': - gp_AttachDrawPlanar(theGraph); - break; - case '2': - gp_AttachK23Search(theGraph); - break; - case '3': - gp_AttachK33Search(theGraph); - break; - case '4': - gp_AttachK4Search(theGraph); - break; - } - - return theGraph; -} - -/**************************************************************************** - ReinitializeGraph() - Internal function that will either reinitialize the given graph or free it - and make a new one just like it. - ****************************************************************************/ - -void ReinitializeGraph(graphP * pGraph, int ReuseGraphs, char command) -{ - if (ReuseGraphs) - gp_ReinitializeGraph(*pGraph); - else { - graphP newGraph = MakeGraph((*pGraph)->N, command); - gp_Free(pGraph); - *pGraph = newGraph; - } -} - -/**************************************************************************** - Creates a random maximal planar graph, then adds 'extraEdges' edges to it. - ****************************************************************************/ - -int RandomGraph(char command, - int extraEdges, - int numVertices, - char * outfileName, - char * outfile2Name) -{ - int Result; - platform_time start, end; - graphP theGraph = NULL, origGraph; - int embedFlags = GetEmbedFlags(command); - char saveEdgeListFormat; - - GetNumberIfZero(&numVertices, "Enter number of vertices:", 1, 1000000); - if ((theGraph = MakeGraph(numVertices, command)) == NULL) - return NOTOK; - - srand(time(NULL)); - - Message("Creating the random graph...\n"); - platform_GetTime(start); - if (gp_CreateRandomGraphEx(theGraph, 3 * numVertices - 6 + extraEdges) != - OK) { - ErrorMessage("gp_CreateRandomGraphEx() failed\n"); - return NOTOK; - } - platform_GetTime(end); - - sprintf(Line, "Created random graph with %d edges in %.3lf seconds. ", - theGraph->M, platform_GetDuration(start, end)); - Message(Line); - FlushConsole(stdout); - - // The user may have requested a copy of the random graph before - // processing - if (outfile2Name != NULL) { - gp_Write(theGraph, outfile2Name, WRITE_ADJLIST); - } - - origGraph = gp_DupGraph(theGraph); - - // Do the requested algorithm on the randomly generated graph - Message("Now processing\n"); - FlushConsole(stdout); - - if (strchr("pdo234", command)) { - platform_GetTime(start); - Result = gp_Embed(theGraph, embedFlags); - platform_GetTime(end); - - gp_SortVertices(theGraph); - - if (gp_TestEmbedResultIntegrity(theGraph, origGraph, Result) != - Result) - Result = NOTOK; - } - else - Result = NOTOK; - - // Write what the algorithm determined and how long it took - WriteAlgorithmResults(theGraph, Result, command, start, end, NULL); - - // On successful algorithm result, write the output file and see if the - // user wants the edge list formatted file. - if (Result == OK || Result == NONEMBEDDABLE) { - if (outfileName != NULL) - gp_Write(theGraph, outfileName, WRITE_ADJLIST); - - Prompt("Do you want to save the generated graph in edge list format " - "(y/n)? "); - fflush(stdin); - scanf(" %c", &saveEdgeListFormat); - if (tolower(saveEdgeListFormat) == 'y') { - char theFileName[256]; - - if (extraEdges > 0) - sprintf(theFileName, "random%cnonPlanarEdgeList.txt", - FILE_DELIMITER); - else - sprintf(theFileName, "random%cmaxPlanarEdgeList.txt", - FILE_DELIMITER); - - sprintf(Line, "Saving edge list format to '%s'\n", theFileName); - Message(Line); - - SaveAsciiGraph(theGraph, theFileName); - } - } - else - ErrorMessage("Failure occurred"); - - gp_Free(&theGraph); - gp_Free(&origGraph); - - FlushConsole(stdout); - return Result; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planaritySpecificGraph.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planaritySpecificGraph.c deleted file mode 100644 index cc9cf2f73..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planaritySpecificGraph.c +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" -#endif -#include "planarity.h" - -/**************************************************************************** - SpecificGraph() - ****************************************************************************/ - -int SpecificGraph(char command, char *infileName, char *outfileName, char *outfile2Name) -{ -graphP theGraph, origGraph; -platform_time start, end; -int Result; - - // Get the filename of the graph to test - if ((infileName = ConstructInputFilename(infileName)) == NULL) - return NOTOK; - - // Create the graph and, if needed, attach the correct algorithm to it - theGraph = gp_New(); - - switch (command) - { - case 'd' : gp_AttachDrawPlanar(theGraph); break; - case '2' : gp_AttachK23Search(theGraph); break; - case '3' : gp_AttachK33Search(theGraph); break; - case '4' : gp_AttachK4Search(theGraph); break; - } - - // Read the graph into memory - Result = gp_Read(theGraph, infileName); - if (Result == NONEMBEDDABLE) - { - Message("The graph contains too many edges.\n"); - // Some of the algorithms will still run correctly with some edges removed. - if (strchr("pdo234", command)) - { - Message("Some edges were removed, but the algorithm will still run correctly.\n"); - Result = OK; - } - } - - // If there was an unrecoverable error, report it - if (Result != OK) - ErrorMessage("Failed to read graph\n"); - - // Otherwise, call the correct algorithm on it - else - { - // Copy the graph for integrity checking - origGraph = gp_DupGraph(theGraph); - - // Run the algorithm - if (strchr("pdo234", command)) - { - int embedFlags = GetEmbedFlags(command); - platform_GetTime(start); - -// gp_CreateDFSTree(theGraph); -// gp_SortVertices(theGraph); -// gp_Write(theGraph, "debug.before.txt", WRITE_DEBUGINFO); -// gp_SortVertices(theGraph); - - Result = gp_Embed(theGraph, embedFlags); - platform_GetTime(end); - Result = gp_TestEmbedResultIntegrity(theGraph, origGraph, Result); - } - else - { - platform_GetTime(start); - Result = NOTOK; - platform_GetTime(end); - } - - // Write what the algorithm determined and how long it took - WriteAlgorithmResults(theGraph, Result, command, start, end, infileName); - - // Free the graph obtained for integrity checking. - gp_Free(&origGraph); - } - - // Report an error, if there was one, free the graph, and return - if (Result != OK && Result != NONEMBEDDABLE) - { - ErrorMessage("AN ERROR HAS BEEN DETECTED\n"); - Result = NOTOK; -// gp_Write(theGraph, "debug.after.txt", WRITE_DEBUGINFO); - } - - // Provide the output file(s) - else - { - // Restore the vertex ordering of the original graph (undo DFS numbering) - if (strchr("pdo234", command)) - gp_SortVertices(theGraph); - - // Determine the name of the primary output file - outfileName = ConstructPrimaryOutputFilename(infileName, outfileName, command); - - // For some algorithms, the primary output file is not always written - if ((strchr("pdo", command) && Result == NONEMBEDDABLE) || - (strchr("234", command) && Result == OK)) - { - // Do not write the file - } - - // Write the primary output file, if appropriate to do so - else - { - gp_Write(theGraph, outfileName, WRITE_ADJLIST); - } - - // NOW WE WANT TO WRITE THE SECONDARY OUTPUT FILE - - // When called from the menu system, we want to write the planar or outerplanar - // obstruction, if one exists. For planar graph drawing, we want the character - // art rendition. An empty but non-NULL string is passed to indicate the necessity - // of selecting a default name for the second output file. - if (outfile2Name != NULL) - { - if ((command == 'p' || command == 'o') && Result == NONEMBEDDABLE) - { - // By default, use the same name as the primary output filename - if (strlen(outfile2Name) == 0) - outfile2Name = outfileName; - gp_Write(theGraph, outfile2Name, WRITE_ADJLIST); - } - else if (command == 'd' && Result == OK) - { - // By default, add ".render.txt" to the primary output filename - if (strlen(outfile2Name) == 0) - strcat((outfile2Name = outfileName), ".render.txt"); - gp_DrawPlanar_RenderToFile(theGraph, outfile2Name); - } - } - } - - // Free the graph - gp_Free(&theGraph); - - // Flush any remaining message content to the user, and return the result - FlushConsole(stdout); - return Result; -} - -/**************************************************************************** - WriteAlgorithmResults() - ****************************************************************************/ - -void WriteAlgorithmResults(graphP theGraph, int Result, char command, platform_time start, platform_time end, char *infileName) -{ - if (infileName) - sprintf(Line, "The graph '%s' ", infileName); - else sprintf(Line, "The graph "); - Message(Line); - - switch (command) - { - case 'p' : sprintf(Line, "is%s planar.\n", Result==OK ? "" : " not"); break; - case 'd' : sprintf(Line, "is%s planar.\n", Result==OK ? "" : " not"); break; - case 'o' : sprintf(Line, "is%s outerplanar.\n", Result==OK ? "" : " not"); break; - case '2' : sprintf(Line, "has %s subgraph homeomorphic to K_{2,3}.\n", Result==OK ? "no" : "a"); break; - case '3' : sprintf(Line, "has %s subgraph homeomorphic to K_{3,3}.\n", Result==OK ? "no" : "a"); break; - case '4' : sprintf(Line, "has %s subgraph homeomorphic to K_4.\n", Result==OK ? "no" : "a"); break; - default : sprintf(Line, "has not been processed due to unrecognized command.\n"); break; - } - Message(Line); - - sprintf(Line, "Algorithm '%s' executed in %.3lf seconds.\n", - GetAlgorithmName(command), platform_GetDuration(start,end)); - Message(Line); -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityUtils.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityUtils.c deleted file mode 100644 index f8829240b..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/planarityUtils.c +++ /dev/null @@ -1,427 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wstrict-prototypes" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" -#pragma clang diagnostic ignored \ - "-Wincompatible-pointer-types-discards-qualifiers" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wold-style-definition" -#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" -#pragma GCC diagnostic ignored "-Wswitch-default" -#pragma GCC diagnostic ignored "-Wunused-result" -#endif -#include "planarity.h" - -/**************************************************************************** - Configuration - ****************************************************************************/ - -char Mode='r', - OrigOut='n', - EmbeddableOut='n', - ObstructedOut='n', - AdjListsForEmbeddingsOut='n', - quietMode='n'; - -void Reconfigure() -{ - fflush(stdin); - - Prompt("\nDo you want to \n" - " Randomly generate graphs (r),\n" - " Specify a graph (s),\n" - " Randomly generate a maximal planar graph (m), or\n" - " Randomly generate a non-planar graph (n)?"); - scanf(" %c", &Mode); - - Mode = tolower(Mode); - if (!strchr("rsmn", Mode)) - Mode = 's'; - - if (Mode == 'r') - { - Message("\nNOTE: The directories for the graphs you want must exist.\n\n"); - - Prompt("Do you want original graphs in directory 'random' (last 10 max)?"); - scanf(" %c", &OrigOut); - - Prompt("Do you want adj. matrix of embeddable graphs in directory 'embedded' (last 10 max))?"); - scanf(" %c", &EmbeddableOut); - - Prompt("Do you want adj. matrix of obstructed graphs in directory 'obstructed' (last 10 max)?"); - scanf(" %c", &ObstructedOut); - - Prompt("Do you want adjacency list format of embeddings in directory 'adjlist' (last 10 max)?"); - scanf(" %c", &AdjListsForEmbeddingsOut); - } - - FlushConsole(stdout); -} - -/**************************************************************************** - MESSAGE - prints a string, but when debugging adds \n and flushes stdout - ****************************************************************************/ - -#define MAXLINE 1024 -char Line[MAXLINE]; - -void Message(char *message) -{ - if (quietMode == 'n') - { - fprintf(stdout, "%s", message); - fflush(stdout); - } -} - -void ErrorMessage(char *message) -{ - if (quietMode == 'n') - { - fprintf(stderr, "%s", message); - fflush(stderr); - } -} - -void FlushConsole(FILE *f) -{ - fflush(f); -} - -void Prompt(char *message) -{ - Message(message); - FlushConsole(stdout); -} - -/**************************************************************************** - ****************************************************************************/ - -void SaveAsciiGraph(graphP theGraph, char *filename) -{ - int e, EsizeOccupied, vertexLabelFix; - FILE *outfile = fopen(filename, WRITETEXT); - - // The filename may specify a directory that doesn't exist - if (outfile == NULL) - { - sprintf(Line, "Failed to write to %s\nMake the directory if not present\n", filename); - ErrorMessage(Line); - return; - } - - // If filename includes path elements, remove them before writing the file's name to the file - if (strrchr(filename, FILE_DELIMITER)) - filename = strrchr(filename, FILE_DELIMITER)+1; - - fprintf(outfile, "%s\n", filename); - - // This edge list file format uses 1-based vertex numbering, and the current code - // internally uses 1-based indexing by default, so this vertex label 'fix' adds zero - // But earlier code used 0-based indexing and added one on output, so we replicate - // that behavior in case the current code has been compiled with zero-based indexing. - vertexLabelFix = 1 - gp_GetFirstVertex(theGraph); - - // Iterate over the edges of the graph - EsizeOccupied = gp_EdgeInUseIndexBound(theGraph); - for (e = gp_GetFirstEdge(theGraph); e < EsizeOccupied; e+=2) - { - // Only output edges that haven't been deleted (i.e. skip the edge holes) - if (gp_EdgeInUse(theGraph, e)) - { - fprintf(outfile, "%d %d\n", - gp_GetNeighbor(theGraph, e) + vertexLabelFix, - gp_GetNeighbor(theGraph, e+1) + vertexLabelFix); - } - } - - // Since vertex numbers are at least 1, this indicates the end of the edge list - fprintf(outfile, "0 0\n"); - - fclose(outfile); -} - -/**************************************************************************** - ****************************************************************************/ - -int TextFilesEqual(char *file1Name, char *file2Name) -{ - FILE *infile1 = NULL, *infile2 = NULL; - int Result = TRUE; - - infile1 = fopen(file1Name, "r"); - infile2 = fopen(file2Name, "r"); - - if (infile1 == NULL || infile2 == NULL) - Result = FALSE; - else - { - int c1=0, c2=0; - - // Read the first file to the end - while ((c1 = fgetc(infile1)) != EOF) - { - // Want to suppress distinction between lines ending with CRLF versus LF - if (c1 == '\r') - continue; - - // Get a char from the second file, except suppress CR again - while ((c2 = fgetc(infile2)) == '\r') - ; - - // If we got a char from the first file, but not from the second - // then the second file is shorter, so files are not equal - if (c2 == EOF) - { - Result = FALSE; - break; - } - - // If we got a char from second file, but not equal to char from - // first file, then files are not equal - if (c1 != c2) - { - Result = FALSE; - break; - } - } - - // If we got to the end of the first file without breaking the loop... - if (c1 == EOF) - { - // Then, once again, suppress CRs first, and then... - while ((c2 = fgetc(infile2)) == '\r') - ; - // Test whether or not the second file also ends, same as the first. - if (fgetc(infile2) != EOF) - Result = FALSE; - } - } - - if (infile1 != NULL) fclose(infile1); - if (infile2 != NULL) fclose(infile2); - return Result; -} - -/**************************************************************************** - ****************************************************************************/ - -int BinaryFilesEqual(char *file1Name, char *file2Name) -{ - FILE *infile1 = NULL, *infile2 = NULL; - int Result = TRUE; - - infile1 = fopen(file1Name, "r"); - infile2 = fopen(file2Name, "r"); - - if (infile1 == NULL || infile2 == NULL) - Result = FALSE; - else - { - int c1=0, c2=0; - - // Read the first file to the end - while ((c1 = fgetc(infile1)) != EOF) - { - // If we got a char from the first file, but not from the second - // then the second file is shorter, so files are not equal - if ((c2 = fgetc(infile2)) == EOF) - { - Result = FALSE; - break; - } - - // If we got a char from second file, but not equal to char from - // first file, then files are not equal - if (c1 != c2) - { - Result = FALSE; - break; - } - } - - // If we got to the end of the first file without breaking the loop... - if (c1 == EOF) - { - // Then attempt to read from the second file to ensure it also ends. - if (fgetc(infile2) != EOF) - Result = FALSE; - } - } - - if (infile1 != NULL) fclose(infile1); - if (infile2 != NULL) fclose(infile2); - return Result; -} - -/**************************************************************************** - ****************************************************************************/ - -int GetEmbedFlags(char command) -{ - int embedFlags = 0; - - switch (command) - { - case 'o' : embedFlags = EMBEDFLAGS_OUTERPLANAR; break; - case 'p' : embedFlags = EMBEDFLAGS_PLANAR; break; - case 'd' : embedFlags = EMBEDFLAGS_DRAWPLANAR; break; - case '2' : embedFlags = EMBEDFLAGS_SEARCHFORK23; break; - case '3' : embedFlags = EMBEDFLAGS_SEARCHFORK33; break; - case '4' : embedFlags = EMBEDFLAGS_SEARCHFORK4; break; - } - - return embedFlags; -} - -/**************************************************************************** - ****************************************************************************/ - -char *GetAlgorithmName(char command) -{ - char *algorithmName = "UnsupportedAlgorithm"; - - switch (command) - { - case 'p' : algorithmName = "PlanarEmbed"; break; - case 'd' : algorithmName = DRAWPLANAR_NAME; break; - case 'o' : algorithmName = "OuterplanarEmbed"; break; - case '2' : algorithmName = K23SEARCH_NAME; break; - case '3' : algorithmName = K33SEARCH_NAME; break; - case '4' : algorithmName = K4SEARCH_NAME; break; - } - - return algorithmName; -} - -/**************************************************************************** - ****************************************************************************/ - -void AttachAlgorithm(graphP theGraph, char command) -{ - switch (command) - { - case 'd' : gp_AttachDrawPlanar(theGraph); break; - case '2' : gp_AttachK23Search(theGraph); break; - case '3' : gp_AttachK33Search(theGraph); break; - case '4' : gp_AttachK4Search(theGraph); break; - } -} - -/**************************************************************************** - A string used to construct input and output filenames. - - The SUFFIXMAXLENGTH is 32 to accommodate ".out.txt" + ".render.txt" + ".test.txt" - ****************************************************************************/ - -#define FILENAMEMAXLENGTH 128 -#define ALGORITHMNAMEMAXLENGTH 32 -#define SUFFIXMAXLENGTH 32 - -char theFileName[FILENAMEMAXLENGTH+1+ALGORITHMNAMEMAXLENGTH+1+SUFFIXMAXLENGTH+1]; - -/**************************************************************************** - ConstructInputFilename() - Returns a string not owned by the caller (do not free string). - String contains infileName content if infileName is non-NULL. - If infileName is NULL, then the user is asked to supply a name. - Returns NULL on error, or a non-NULL string on success. - ****************************************************************************/ - -char *ConstructInputFilename(char *infileName) -{ - if (infileName == NULL) - { - Prompt("Enter graph file name: "); - fflush(stdin); - scanf(" %s", theFileName); - - if (!strchr(theFileName, '.')) - strcat(theFileName, ".txt"); - } - else - { - if (strlen(infileName) > FILENAMEMAXLENGTH) - { - ErrorMessage("Filename is too long"); - return NULL; - } - strcpy(theFileName, infileName); - } - - return theFileName; -} - -/**************************************************************************** - ConstructPrimaryOutputFilename() - Returns a string not owned by the caller (do not free string). - Reuses the same memory space as ConstructinputFilename(). - If outfileName is non-NULL, then the result string contains its content. - If outfileName is NULL, then the infileName and the command's algorithm name - are used to construct a string. - Returns non-NULL string - ****************************************************************************/ - -char *ConstructPrimaryOutputFilename(char *infileName, char *outfileName, char command) -{ - char *algorithmName = GetAlgorithmName(command); - - if (outfileName == NULL) - { - // The output filename is based on the input filename - if (theFileName != infileName) - strcpy(theFileName, infileName); - - // If the primary output filename has not been given, then we use - // the input filename + the algorithm name + a simple suffix - if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH) - { - strcat(theFileName, "."); - strcat(theFileName, algorithmName); - } - else - ErrorMessage("Algorithm Name is too long, so it will not be used in output filename."); - - strcat(theFileName, ".out.txt"); - } - else - { - if (strlen(outfileName) > FILENAMEMAXLENGTH) - { - // The output filename is based on the input filename - if (theFileName != infileName) - strcpy(theFileName, infileName); - - if (strlen(algorithmName) <= ALGORITHMNAMEMAXLENGTH) - { - strcat(theFileName, "."); - strcat(theFileName, algorithmName); - } - strcat(theFileName, ".out.txt"); - sprintf(Line, "Outfile filename is too long. Result placed in %s", theFileName); - ErrorMessage(Line); - } - else - { - if (theFileName != outfileName) - strcpy(theFileName, outfileName); - } - } - - return theFileName; -} -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/platformTime.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/platformTime.h deleted file mode 100644 index 14453f4fd..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/platformTime.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef PLATFORM_TIME -#define PLATFORM_TIME - -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifdef WIN32 - -#include -#include - -#define platform_time DWORD -#define platform_GetTime(timeVar) (timeVar = GetTickCount()) -#define platform_GetDuration(startTime, endTime) ((double) (endTime-startTime) / 1000.0) - -#else - -#include - -typedef struct { - clock_t hiresTime; - time_t lowresTime; -} platform_time; - -#define platform_GetTime(timeVar) (timeVar.hiresTime = clock(), timeVar.lowresTime = time(NULL)) - -// Many flavors of Unix have CLOCKS_PER_SEC at 1 million, and clock_t as a 4 byte long integer -// which means that the clock() construct has a resolution of only about 2000 seconds -// If we're getting a duration longer than that, then we fall back to the coarser time() measure - -#define platform_GetDuration(startTime, endTime) ( \ - ( (double) (endTime.lowresTime - startTime.lowresTime) ) > 2000 ? \ - ( (double) (endTime.lowresTime - startTime.lowresTime) ) : \ - ( (double) (endTime.hiresTime - startTime.hiresTime)) / CLOCKS_PER_SEC) - -/* -#define platform_time clock_t -#define platform_GetTime() clock() -#define platform_GetDuration(startTime, endTime) (((double) (endTime - startTime)) / CLOCKS_PER_SEC) -*/ - -/* -#define platform_time time_t -#define platform_GetTime() time((time_t *)NULL) -#define platform_GetDuration(startTime, endTime) ((double) (endTime - startTime)) -*/ - -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.c b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.c deleted file mode 100644 index ce1c04e7b..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.c +++ /dev/null @@ -1,223 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#include "appconst.h" -#include "stack.h" -#include - -stackP sp_New(int capacity) -{ -stackP theStack; - - theStack = (stackP) malloc(sizeof(stack)); - - if (theStack != NULL) - { - theStack->S = (int *) malloc(capacity*sizeof(int)); - if (theStack->S == NULL) - { - free(theStack); - theStack = NULL; - } - } - - if (theStack != NULL) - { - theStack->capacity = capacity; - sp_ClearStack(theStack); - } - - return theStack; -} - -void sp_Free(stackP *pStack) -{ - if (pStack == NULL || *pStack == NULL) return; - - (*pStack)->capacity = (*pStack)->size = 0; - - if ((*pStack)->S != NULL) - free((*pStack)->S); - (*pStack)->S = NULL; - free(*pStack); - - *pStack = NULL; -} - -int sp_CopyContent(stackP stackDst, stackP stackSrc) -{ - if (stackDst->capacity < stackSrc->size) - return NOTOK; - - if (stackSrc->size > 0) - memcpy(stackDst->S, stackSrc->S, stackSrc->size*sizeof(int)); - - stackDst->size = stackSrc->size; - return OK; -} - -stackP sp_Duplicate(stackP theStack) -{ -stackP newStack = sp_New(theStack->capacity); - - if (newStack == NULL) - return NULL; - - if (theStack->size > 0) - { - memcpy(newStack->S, theStack->S, theStack->size*sizeof(int)); - newStack->size = theStack->size; - } - - return newStack; -} - -int sp_Copy(stackP stackDst, stackP stackSrc) -{ - if (sp_CopyContent(stackDst, stackSrc) != OK) - { - stackP newStack = sp_Duplicate(stackSrc); - int *p; - - if (newStack == NULL) - return NOTOK; - - p = stackDst->S; - stackDst->S = newStack->S; - newStack->S = p; - newStack->capacity = stackDst->capacity; - sp_Free(&newStack); - - stackDst->size = stackSrc->size; - stackDst->capacity = stackSrc->capacity; - } - - return OK; -} - -#ifndef SPEED_MACROS - -int sp_ClearStack(stackP theStack) -{ - theStack->size = 0; - return OK; -} - -int sp_GetCurrentSize(stackP theStack) -{ - return theStack->size; -} - -int sp_SetCurrentSize(stackP theStack, int size) -{ - return size > theStack->capacity ? NOTOK : (theStack->size = size, OK); -} - -int sp_IsEmpty(stackP theStack) -{ - return !theStack->size; -} - -int sp_NonEmpty(stackP theStack) -{ - return theStack->size; -} - -int sp__Push(stackP theStack, int a) -{ - if (theStack->size >= theStack->capacity) - return NOTOK; - - theStack->S[theStack->size++] = a; - return OK; -} - -int sp__Push2(stackP theStack, int a, int b) -{ - if (theStack->size + 1 >= theStack->capacity) - return NOTOK; - - theStack->S[theStack->size++] = a; - theStack->S[theStack->size++] = b; - return OK; -} - -int sp__Pop(stackP theStack, int *pA) -{ - if (theStack->size <= 0) - return NOTOK; - - *pA = theStack->S[--theStack->size]; - return OK; -} - -int sp__Pop_Discard(stackP theStack) -{ - if (theStack->size <= 0) - return NOTOK; - - --theStack->size; - return OK; -} - -int sp__Pop2(stackP theStack, int *pA, int *pB) -{ - if (theStack->size <= 1) - return NOTOK; - - *pB = theStack->S[--theStack->size]; - *pA = theStack->S[--theStack->size]; - - return OK; -} - -int sp__Pop2_Discard1(stackP theStack, int *pA) -{ - if (theStack->size <= 1) - return NOTOK; - - // When a pair of the form (main, secondary) are pushed in order, - // it is sometimes necessary to pop the secondary and discard, - // then pop and store the main datum. - --theStack->size; - *pA = theStack->S[--theStack->size]; - - return OK; -} - -int sp__Pop2_Discard(stackP theStack) -{ - if (theStack->size <= 1) - return NOTOK; - - --theStack->size; - --theStack->size; - - return OK; -} - -int sp_Top(stackP theStack) -{ - return theStack->size ? theStack->S[theStack->size-1] : NIL; -} - -int sp_Get(stackP theStack, int pos) -{ - if (theStack == NULL || pos < 0 || pos >= theStack->size) - return NOTOK; - - return (theStack->S[pos]); -} - -int sp_Set(stackP theStack, int pos, int val) -{ - if (theStack == NULL || pos < 0 || pos >= theStack->size) - return NOTOK; - - return (theStack->S[pos] = val); -} - -#endif // not defined SPEED_MACROS diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.h b/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.h deleted file mode 100644 index e6e87f910..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/c/stack.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright (c) 1997-2020, John M. Boyer -All rights reserved. -See the LICENSE.TXT file for licensing information. -*/ - -#ifndef STACK_H -#define STACK_H - -#ifdef __cplusplus -extern "C" { -#endif - -// includes mem functions like memcpy -#include - -typedef struct -{ - int *S; - int size, capacity; -} stack; - -typedef stack * stackP; - -stackP sp_New(int); -void sp_Free(stackP *); - -int sp_Copy(stackP, stackP); - -int sp_CopyContent(stackP stackDst, stackP stackSrc); -stackP sp_Duplicate(stackP theStack); - -#define sp_GetCapacity(theStack) (theStack->capacity) - -#ifndef SPEED_MACROS - -int sp_ClearStack(stackP); -int sp_GetCurrentSize(stackP theStack); -int sp_SetCurrentSize(stackP theStack, int top); - -int sp_IsEmpty(stackP); -int sp_NonEmpty(stackP); - -#define sp_Push(theStack, a) { if (sp__Push(theStack, (a)) != OK) return NOTOK; } -#define sp_Push2(theStack, a, b) { if (sp__Push2(theStack, (a), (b)) != OK) return NOTOK; } - -int sp__Push(stackP, int); -int sp__Push2(stackP, int, int); - -#define sp_Pop(theStack, a) { if (sp__Pop(theStack, &(a)) != OK) return NOTOK; } -#define sp_Pop_Discard(theStack) { if (sp__Pop_Discard(theStack) != OK) return NOTOK; } - -#define sp_Pop2(theStack, a, b) { if (sp__Pop2(theStack, &(a), &(b)) != OK) return NOTOK; } -#define sp_Pop2_Discard1(theStack, a) { if (sp__Pop2_Discard1(theStack, &(a)) != OK) return NOTOK; } -#define sp_Pop2_Discard(theStack) { if (sp__Pop2_Discard(theStack) != OK) return NOTOK; } - -int sp__Pop(stackP, int *); -int sp__Pop_Discard(stackP theStack); - -int sp__Pop2(stackP, int *, int *); -int sp__Pop2_Discard1(stackP theStack, int *pA); -int sp__Pop2_Discard(stackP theStack); - -int sp_Top(stackP); -int sp_Get(stackP, int); -int sp_Set(stackP, int, int); - -#else - -#define sp_ClearStack(theStack) theStack->size=0 -#define sp_GetCurrentSize(theStack) (theStack->size) -#define sp_SetCurrentSize(theStack, Size) ((Size) > theStack->capacity ? NOTOK : (theStack->size = (Size), OK)) - -#define sp_IsEmpty(theStack) !theStack->size -#define sp_NonEmpty(theStack) theStack->size - -#define sp_Push(theStack, a) theStack->S[theStack->size++] = a -#define sp_Push2(theStack, a, b) {sp_Push(theStack, a); sp_Push(theStack, b);} - -#define sp_Pop(theStack, a) a=theStack->S[--theStack->size] -#define sp_Pop_Discard(theStack) --theStack->size - -#define sp_Pop2(theStack, a, b) {sp_Pop(theStack, b);sp_Pop(theStack, a);} -#define sp_Pop2_Discard1(theStack, a) {sp_Pop_Discard(theStack);sp_Pop(theStack, a);} -#define sp_Pop2_Discard(theStack) {sp_Pop_Discard(theStack);sp_Pop_Discard(theStack);} - -#define sp_Top(theStack) (theStack->size ? theStack->S[theStack->size-1] : NIL) -#define sp_Get(theStack, pos) (theStack->S[pos]) -#define sp_Set(theStack, pos, val) (theStack->S[pos] = val) - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/configure.ac b/extern/edge-addition-planarity-suite-Version_3.0.1.0/configure.ac deleted file mode 100644 index dbe67e253..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/configure.ac +++ /dev/null @@ -1,31 +0,0 @@ -AC_INIT([planarity],[3.0.1.0],[jboyer@acm.org]) -AM_INIT_AUTOMAKE([subdir-objects] [foreign]) -AC_CONFIG_MACRO_DIRS([m4]) -AC_CONFIG_SRCDIR([c/]) - -# The version of the libtool library is of the form current:revision:age -# -# See http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -# -# When doing a release, they should be updated like this: -# 1. If no interfaces changed, only implementations: just increment -# revision. -# 2. If interfaces were added, none removed: increment current, set -# revision to zero and increment age. -# 3. If interfaces were removed (breaks backward compatibility): increment -# current, and set both revision and age to zero. -LT_CURRENT=0 -LT_REVISION=1 -LT_AGE=0 -AC_SUBST(LT_CURRENT) -AC_SUBST(LT_REVISION) -AC_SUBST(LT_AGE) - -AC_PROG_CC -AC_PROG_INSTALL -LT_INIT - -AC_CHECK_HEADERS([ctype.h stdio.h stdlib.h string.h time.h unistd.h]) - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/extern/edge-addition-planarity-suite-Version_3.0.1.0/planarity.1 b/extern/edge-addition-planarity-suite-Version_3.0.1.0/planarity.1 deleted file mode 100644 index 7cde0d5a1..000000000 --- a/extern/edge-addition-planarity-suite-Version_3.0.1.0/planarity.1 +++ /dev/null @@ -1,92 +0,0 @@ -.TH planarity 1 - -.SH NAME -planarity - The Edge Addition Planarity Suite - -.SH SYNOPSIS -.B planarity -.B [-q] -.B [-h|-help] -.B [-i|-info] -.B [-r] -.B [-s] -.B [-rm] -.B [-rn] - -.SH DESCRIPTION -Invokes the Edge Addition Planarity Suite commandline tool. - -Without argument, the tool presents a menu-driven interactive -interface. - -When used in batch mode, the tool returns 0 for a planar graph, 1 for -a nonplanar graph and -1 on error. - -.SH OPTIONS -.TP -.B -q -Quiet mode -.TP -.B -h, -help -Display some help -.TP -.B -i, -info -Display copyright, license and reference articles information - -.SH FEATURES -Features in batch mode. -.TP -.B -r C K N -Run the C command (see below) on K random graphs with N vertices -.TP -.B -s C I O [O2] -Run the C command (see below) on a specific graph given in the I input -file, with output in the primary O file and complementary information -in the secondary O2 file. -.TP -.B -rm N O [O2] -Compute a maximal planar random graph on N vertices with output in the -primary O file and optionally the chosen graph in the O2 file. -.TP -.B -rn N O [O2] -Compute a nonplanar random graph (maximal planar and an edge) on N -vertices with output in the primary O file and optionally the chosen -graph in the O2 file. -.SH COMMANDS -Determine which algorithm implementation to run -.TP -.B -p -Planar embedding and Kuratowski subgraph isolation -.TP -.B -d -Planar graph drawing by visibility representation -.TP -.B -o -Outerplanar embedding and obstruction isolation -.TP -.B -2 -Search for subgraph homeomorphic to K_{2,3} -.TP -.B -3 -Search for subgraph homeomorphic to K_{3,3} -.TP -.B -4 -Search for subgraph homeomorphic to K_4 -.TP -.B -a -All of the above - -.SH EXAMPLES -.TP -.B planarity -s -q -p infile.txt embedding.out [obstruction.out] -Process infile.txt in quiet mode (-q), putting planar embedding in -embedding.out or (optionally) a Kuratowski subgraph in Obstruction.out -Process returns 0=planar, 1=nonplanar, -1=error -.TP -.B planarity -s -q -d infile.txt embedding.out [drawing.out] -If graph in infile.txt is planar, then put embedding in embedding.out -and (optionally) an ASCII art drawing in drawing.out -Process returns 0=planar, 1=nonplanar, -1=error - -.SH SEE ALSO -\fBplanarity -h -menu\fR for more information. From d7a52f4d1c9482b9f218bdd37c3ad8c6af3c8962 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 11:54:53 +0200 Subject: [PATCH 4/7] Format --- src/digraphs.c | 8 +- src/planar.c | 381 ++++++++++++++++++++++++------------------------- 2 files changed, 188 insertions(+), 201 deletions(-) diff --git a/src/digraphs.c b/src/digraphs.c index ba0011b31..ca5b7498f 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -1359,10 +1359,10 @@ static Obj FuncDIGRAPH_REFLEX_TRANS_CLOSURE(Obj self, Obj digraph) { } static Obj FuncRANDOM_DIGRAPH(Obj self, Obj nn, Obj pp) { - UInt n, i, j; - Double p, q; - Int len; - Obj adj, adji; + UInt n, i, j; + Double p, q; + Int len; + Obj adj, adji; n = INT_INTOBJ(nn); p = VAL_MACFLOAT(pp); diff --git a/src/planar.c b/src/planar.c index 7c897f6f7..18ddb1d2b 100644 --- a/src/planar.c +++ b/src/planar.c @@ -12,19 +12,19 @@ #include "planar.h" // C headers -#include // for INT_MAX -#include // for true and false +#include // for INT_MAX +#include // for true and false // Digraphs package headers -#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY -#include "digraphs-debug.h" // for DIGRAPHS_ASSERT -#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . -#include "gap-includes.h" // for Fail, False, True +#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . +#include "gap-includes.h" // for Fail, False, True // edge-addition-planarity-suite headers #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored \ +#pragma clang diagnostic ignored \ "-Wincompatible-pointer-types-discards-qualifiers" #pragma clang diagnostic ignored "-Wstrict-prototypes" #elif defined(__GNUC__) @@ -50,237 +50,224 @@ Obj trivial_planarity_output(Int V, bool krtwsk); Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk); // GAP level functions -Obj FuncIS_PLANAR(Obj self, Obj digraph) -{ - return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); +Obj FuncIS_PLANAR(Obj self, Obj digraph) { + return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); } -Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) -{ - return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); +Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) { + return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); } -Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } // The implementation of the main functions in this file. -Obj trivial_planarity_output(Int V, bool krtwsk) -{ - Obj res; - if (krtwsk) { - Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); - SET_LEN_PLIST(subgraph, V); - for (int i = 1; i <= V; ++i) { - Obj list = NEW_PLIST_IMM(T_PLIST, 0); - SET_LEN_PLIST(list, 0); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); - } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, True); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } - else { - res = True; +Obj trivial_planarity_output(Int V, bool krtwsk) { + Obj res; + if (krtwsk) { + Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); + SET_LEN_PLIST(subgraph, V); + for (int i = 1; i <= V; ++i) { + Obj list = NEW_PLIST_IMM(T_PLIST, 0); + SET_LEN_PLIST(list, 0); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); } - return res; + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, True); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } else { + res = True; + } + return res; } // This function only accepts digraphs without multiple edges -Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) -{ - DIGRAPHS_ASSERT( - flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR || - flags == EMBEDFLAGS_SEARCHFORK23 || flags == EMBEDFLAGS_SEARCHFORK4 || - flags == EMBEDFLAGS_SEARCHFORK33); +Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { + DIGRAPHS_ASSERT(flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR + || flags == EMBEDFLAGS_SEARCHFORK23 + || flags == EMBEDFLAGS_SEARCHFORK4 + || flags == EMBEDFLAGS_SEARCHFORK33); - if (CALL_1ARGS(IsDigraph, digraph) != True) { - ErrorQuit( - "Digraphs: boyers_planarity_check (C): the 1st argument must be " - "a digraph, not %s", - (Int)TNAM_OBJ(digraph), 0L); - } - if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { - ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); - } + if (CALL_1ARGS(IsDigraph, digraph) != True) { + ErrorQuit("Digraphs: boyers_planarity_check (C): the 1st argument must be " + "a digraph, not %s", + (Int) TNAM_OBJ(digraph), + 0L); + } + if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { + ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); + } - Int V = DigraphNrVertices(digraph); - if (V == 0) { - return trivial_planarity_output(0, krtwsk); - } + Int V = DigraphNrVertices(digraph); + if (V == 0) { + return trivial_planarity_output(0, krtwsk); + } - Int E = DigraphNrAdjacenciesWithoutLoops(digraph); - if (E == 0) { - return trivial_planarity_output(V, krtwsk); - } - if (V > INT_MAX) { - // Cannot currently test this, it might always be true, depending on - // the definition of Int. - ErrorQuit( - "Digraphs: boyers_planarity_check (C): the maximum number of " - "nodes is %d, found %d", - INT_MAX, V); - return 0L; - } - else if (2 * E > INT_MAX) { - // Cannot currently test this - ErrorQuit( - "Digraphs: boyers_planarity_check (C): the maximum number of " - "edges is %d, found %d", - INT_MAX / 2, E); - return 0L; - } + Int E = DigraphNrAdjacenciesWithoutLoops(digraph); + if (E == 0) { + return trivial_planarity_output(V, krtwsk); + } + if (V > INT_MAX) { + // Cannot currently test this, it might always be true, depending on + // the definition of Int. + ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " + "nodes is %d, found %d", + INT_MAX, + V); + return 0L; + } else if (2 * E > INT_MAX) { + // Cannot currently test this + ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " + "edges is %d, found %d", + INT_MAX / 2, + E); + return 0L; + } - graphP theGraph = gp_New(); + graphP theGraph = gp_New(); - if (gp_InitGraph(theGraph, V) != OK) { - gp_Free(&theGraph); - ErrorQuit( - "Digraphs: boyers_planarity_check (C): invalid number of nodes!", - 0L, 0L); - return 0L; - } - else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { - gp_Free(&theGraph); - ErrorQuit( - "Digraphs: boyers_planarity_check (C): invalid number of edges!", - 0L, 0L); - return 0L; - } + if (gp_InitGraph(theGraph, V) != OK) { + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", + 0L, + 0L); + return 0L; + } else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of edges!", + 0L, + 0L); + return 0L; + } - switch (flags) { + switch (flags) { case EMBEDFLAGS_SEARCHFORK33: - gp_AttachK33Search(theGraph); - break; + gp_AttachK33Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK23: - gp_AttachK23Search(theGraph); - break; + gp_AttachK23Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK4: - gp_AttachK4Search(theGraph); - break; + gp_AttachK4Search(theGraph); + break; default: - break; - } + break; + } - int status; - Obj const out = FuncOutNeighbours(0L, digraph); + int status; + Obj const out = FuncOutNeighbours(0L, digraph); - // Construct the antisymmetric digraph with no loops - for (Int v = 1; v <= LEN_LIST(out); ++v) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); - gp_SetVertexIndex(theGraph, v, v); - Obj const out_v = ELM_LIST(out, v); - for (Int w = 1; w <= LEN_LIST(out_v); ++w) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); - Int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v < u || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), - INTOBJ_INT(v)) == False) { - status = gp_AddEdge(theGraph, v, 0, u, 0); - if (status != OK) { - // Cannot currently test this, i.e. it shouldn't happen - // (and currently there is no example where it does - // happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): " - "internal error, " - "can't add edge from %d to %d", - (Int)v, (Int)u); - return 0L; - } - } + // Construct the antisymmetric digraph with no loops + for (Int v = 1; v <= LEN_LIST(out); ++v) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); + gp_SetVertexIndex(theGraph, v, v); + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u + || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) + == False) { + status = gp_AddEdge(theGraph, v, 0, u, 0); + if (status != OK) { + // Cannot currently test this, i.e. it shouldn't happen + // (and currently there is no example where it does + // happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): " + "internal error, " + "can't add edge from %d to %d", + (Int) v, + (Int) u); + return 0L; } + } } + } - status = gp_Embed(theGraph, flags); - if (status == NOTOK) { - // Cannot currently test this, i.e. it shouldn't happen (and - // currently there is no example where it does happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", - 0L, 0L); - } + status = gp_Embed(theGraph, flags); + if (status == NOTOK) { + // Cannot currently test this, i.e. it shouldn't happen (and + // currently there is no example where it does happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", 0L, 0L); + } - // Construct the return value - Obj res; - if (krtwsk) { - // Kuratowski subgraph isolator - gp_SortVertices(theGraph); - Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); - SET_LEN_PLIST(subgraph, theGraph->N); - for (int i = 1; i <= theGraph->N; ++i) { - int nr = 0; - Obj list = NEW_PLIST(T_PLIST, 0); - int j = theGraph->V[i].link[1]; - while (j) { - if (CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT((Int)i), - INTOBJ_INT((Int)theGraph->E[j].neighbor)) == - True) { - AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); - } - j = theGraph->E[j].link[1]; - } - if (nr == 0) { - SET_LEN_PLIST(list, 0); - } - MakeImmutable(list); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); + // Construct the return value + Obj res; + if (krtwsk) { + // Kuratowski subgraph isolator + gp_SortVertices(theGraph); + Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); + SET_LEN_PLIST(subgraph, theGraph->N); + for (int i = 1; i <= theGraph->N; ++i) { + int nr = 0; + Obj list = NEW_PLIST(T_PLIST, 0); + int j = theGraph->V[i].link[1]; + while (j) { + if (CALL_3ARGS(IsDigraphEdge, + digraph, + INTOBJ_INT((Int) i), + INTOBJ_INT((Int) theGraph->E[j].neighbor)) + == True) { + AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } - else if (status == NONEMBEDDABLE) { - res = False; + j = theGraph->E[j].link[1]; + } + if (nr == 0) { + SET_LEN_PLIST(list, 0); + } + MakeImmutable(list); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); } - else { - res = True; - } - gp_Free(&theGraph); - return res; + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } else if (status == NONEMBEDDABLE) { + res = False; + } else { + res = True; + } + gp_Free(&theGraph); + return res; } From 793e7cab428e005a90873b43e06dbe132d7cb944 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 14:11:07 +0200 Subject: [PATCH 5/7] Start to fix compiler warnings --- .../c/graphLib/graphDFSUtils.c | 6 +- .../c/graphLib/graphLib.h | 2 +- .../c/graphLib/graphUtils.c | 18 +- .../homeomorphSearch/graphK33Search.c | 5 +- .../graphLib/homeomorphSearch/graphK4Search.c | 2 +- .../c/graphLib/io/g6-read-iterator.c | 7 +- .../c/graphLib/io/strOrFile.c | 2 +- .../c/graphLib/io/strOrFile.h | 2 +- .../c/graphLib/io/strbuf.c | 2 +- .../c/graphLib/io/strbuf.h | 64 +-- .../c/graphLib/lowLevelUtils/apiutils.c | 7 +- .../c/graphLib/lowLevelUtils/apiutils.h | 4 +- .../planarityRelated/graphDrawPlanar.c | 4 +- .../c/graphLib/planarityRelated/graphEmbed.c | 5 + .../graphLib/planarityRelated/graphIsolator.c | 2 + .../planarityRelated/graphNonplanar.c | 1 + .../c/graphLib/planarityRelated/graphTests.c | 8 +- m4/ax_check_planarity.m4 | 14 +- src/planar.c | 385 +++++++++--------- 19 files changed, 292 insertions(+), 248 deletions(-) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c index 69d824363..cafb3a69a 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphDFSUtils.c @@ -26,6 +26,8 @@ extern void _ClearVertexVisitedFlags(graphP theGraph, int); #include "lowLevelUtils/platformTime.h" +int _SortVertices(graphP theGraph); + int gp_CreateDFSTree(graphP theGraph) { stackP theStack; @@ -376,7 +378,8 @@ int gp_LowpointAndLeastAncestor(graphP theGraph) graphs that may be designed and stored in a DFI sorted format. ********************************************************************/ -int gp_LeastAncestor(graphP theGraph) +#if false +static int gp_LeastAncestor(graphP theGraph) { stackP theStack = theGraph->theStack; int v, u, uneighbor, e, leastAncestor; @@ -455,3 +458,4 @@ int gp_LeastAncestor(graphP theGraph) return OK; } +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h index 275c964e3..9ccbc986b 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphLib.h @@ -56,4 +56,4 @@ extern "C" } #endif -#endif \ No newline at end of file +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c index 14f372031..bfe489cd5 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/graphUtils.c @@ -34,6 +34,12 @@ void _RestoreEdge(graphP theGraph, int arcPos); int _ContractEdge(graphP theGraph, int e); int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore); int _RestoreVertex(graphP theGraph); +int _ClearVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +int _SetVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x); +int _ComputeArcType(graphP theGraph, int a, int b, int edgeType); +int _SetEdgeType(graphP theGraph, int u, int v); + +int debugNOTOK(void); /******************************************************************** Private functions, except exported within library @@ -286,7 +292,7 @@ void _InitVertices(graphP theGraph) memset(theGraph->extFace, NIL_CHAR, gp_VertexIndexBound(theGraph) * sizeof(extFaceLinkRec)); for (v = gp_GetFirstVertex(theGraph); gp_VertexInRange(theGraph, v); v++) - gp_InitVertexFlags(theGraph, v); + gp_InitVertexFlags(theGraph, v); #endif // N.B. This is the legacy API-based approach to initializing the vertices // int v; @@ -1178,7 +1184,7 @@ int _GetRandomNumber(int NMin, int NMax) ********************************************************************/ -int _getUnprocessedChild(graphP theGraph, int parent) +static int _getUnprocessedChild(graphP theGraph, int parent) { int e = gp_GetFirstArc(theGraph, parent); int eTwin = gp_GetTwinArc(theGraph, e); @@ -1227,7 +1233,8 @@ int _getUnprocessedChild(graphP theGraph, int parent) unless the given vertex has an unprocessed child. ********************************************************************/ -int _hasUnprocessedChild(graphP theGraph, int parent) +#if false +static int _hasUnprocessedChild(graphP theGraph, int parent) { int e = gp_GetFirstArc(theGraph, parent); @@ -1239,6 +1246,7 @@ int _hasUnprocessedChild(graphP theGraph, int parent) return 1; } +#endif /******************************************************************** gp_CreateRandomGraphEx() @@ -1821,7 +1829,7 @@ int gp_DeleteEdge(graphP theGraph, int e, int nextLink) from the stack for restoration. ********************************************************************/ -void _RestoreArc(graphP theGraph, int arc) +static void _RestoreArc(graphP theGraph, int arc) { int nextArc = gp_GetNextArc(theGraph, arc), prevArc = gp_GetPrevArc(theGraph, arc); @@ -2259,7 +2267,7 @@ int _IdentifyVertices(graphP theGraph, int u, int v, int eBefore) Returns OK for success, NOTOK for internal failure. ********************************************************************/ -int gp_RestoreVertex(graphP theGraph) +static int gp_RestoreVertex(graphP theGraph) { return theGraph->functions.fpRestoreVertex(theGraph); } diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c index 71ad4cb68..b62b1c0b6 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK33Search.c @@ -59,6 +59,8 @@ extern int _AddAndMarkUnembeddedEdges(graphP theGraph); extern void _K33Search_InitEdgeRec(K33SearchContext *context, int e); +int _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d); + /* Private functions for K_{3,3} searching. */ int _SearchForK33InBicomp(graphP theGraph, K33SearchContext *context, int v, int R); @@ -75,7 +77,6 @@ int _FindExternalConnectionDescendantEndpoint(graphP theGraph, int ancestor, int _SearchForMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int *pMergeBlocker); int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, int mergeBlocker); -int _MarkLowestXYPath(graphP theGraph); int _TestForZtoWPath(graphP theGraph); int _TestForStraddlingBridge(graphP theGraph, K33SearchContext *context, int u_max); int _K33Search_DeleteUnmarkedEdgesInBicomp(graphP theGraph, K33SearchContext *context, int BicompRoot); @@ -918,7 +919,7 @@ int _FindK33WithMergeBlocker(graphP theGraph, K33SearchContext *context, int v, The function returns NOTOK on internal error, OK otherwise. Preconditions: The X-Y path is marked visited by a prior invocation of - the meothd _MarkLowestXYPath() above. + the method _MarkLowestXYPath() above. So, we start a depth first search from W to find a visited vertex, except we prune the search to ignore vertices whose obstruction type is other than diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c index 396229edd..1faf2fef0 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/homeomorphSearch/graphK4Search.c @@ -1018,7 +1018,7 @@ int _K4_ReducePathComponent(graphP theGraph, K4SearchContext *context, int R, in marked for isolation. ********************************************************************/ -int _K4_DeleteEdge(graphP theGraph, K4SearchContext *context, int e, int nextLink) +static int _K4_DeleteEdge(graphP theGraph, K4SearchContext *context, int e, int nextLink) { _K4Search_InitEdgeRec(context, e); _K4Search_InitEdgeRec(context, gp_GetTwinArc(theGraph, e)); diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c index e97fd44f9..3c1744815 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c @@ -226,6 +226,7 @@ int _beginG6ReadIteration(G6ReadIterator *pG6ReadIterator) int _processAndCheckHeader(strOrFileP g6Input) { + char const*correctG6Header,*sparse6Header,*digraph6Header; int exitCode = OK; if (g6Input == NULL) @@ -234,9 +235,9 @@ int _processAndCheckHeader(strOrFileP g6Input) return NOTOK; } - char *correctG6Header = ">>graph6<<"; - char *sparse6Header = ">>sparse6<"; - char *digraph6Header = ">>digraph6"; + correctG6Header = ">>graph6<<"; + sparse6Header = ">>sparse6<"; + digraph6Header = ">>digraph6"; char headerCandidateChars[11]; headerCandidateChars[0] = '\0'; diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c index f617ac7f5..c0f141c90 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c @@ -21,7 +21,7 @@ See the LICENSE.TXT file for licensing information. Returns the allocated string-or-file container, or NULL on error. ********************************************************************/ -strOrFileP sf_New(char *theStr, char *fileName, char *ioMode) +strOrFileP sf_New(char const*theStr, char const*fileName, char const*ioMode) { strOrFileP theStrOrFile; int containerType = 0; diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h index 8a2252386..70ee2cdd0 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.h @@ -30,7 +30,7 @@ extern "C" typedef strOrFile *strOrFileP; - strOrFileP sf_New(char *theStr, char *fileName, char *ioMode); + strOrFileP sf_New(char const*theStr, char const*fileName, char const*ioMode); int sf_ValidateStrOrFile(strOrFileP theStrOrFile); char sf_getc(strOrFileP theStrOrFile); diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c index eea9e50b2..efe07f6dd 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.c @@ -180,7 +180,7 @@ void sb_ReadSkipInteger(strBufP theStrBuf) small concatenations). Returns OK on success, NOTOK on error ********************************************************************/ -int sb_ConcatString(strBufP theStrBuf, char *s) +int sb_ConcatString(strBufP theStrBuf, char const*s) { int slen = s == NULL ? 0 : strlen(s); diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h index 28783222c..24d1f68ac 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strbuf.h @@ -8,56 +8,60 @@ See the LICENSE.TXT file for licensing information. #define STRBUF_H #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif // includes mem functions like memcpy #include - typedef struct - { - char *buf; - int size, capacity, readPos; - } strBuf; +typedef struct { + char * buf; + int size, capacity, readPos; +} strBuf; - typedef strBuf *strBufP; +typedef strBuf * strBufP; - strBufP sb_New(int); - void sb_Free(strBufP *); +strBufP sb_New(int); +void sb_Free(strBufP *); - void sb_ClearBuf(strBufP); - int sb_Copy(strBufP, strBufP); - strBufP sb_Duplicate(strBufP); +void sb_ClearBuf(strBufP); +int sb_Copy(strBufP, strBufP); +strBufP sb_Duplicate(strBufP); #define sb_GetFullString(theStrBuf) (theStrBuf->buf) #define sb_GetSize(theStrBuf) (theStrBuf->size) #define sb_GetCapacity(theStrBuf) (theStrBuf->capacity) -#define sb_GetUnreadCharCount(theStrBuf) (theStrBuf->size - theStrBuf->readPos) -#define sb_GetReadString(theStrBuf) ((theStrBuf != NULL && theStrBuf->buf != NULL) ? (theStrBuf->buf + theStrBuf->readPos) : NULL) +#define sb_GetUnreadCharCount(theStrBuf) \ + (theStrBuf->size - theStrBuf->readPos) +#define sb_GetReadString(theStrBuf) \ + ((theStrBuf != NULL && theStrBuf->buf != NULL) \ + ? (theStrBuf->buf + theStrBuf->readPos) \ + : NULL) #define sb_GetReadPos(theStrBuf) (theStrBuf->readPos) -#define sb_SetReadPos(theStrBuf, theReadPos) \ - { \ - theStrBuf->readPos = theReadPos; \ - } +#define sb_SetReadPos(theStrBuf, theReadPos) \ + { \ + theStrBuf->readPos = theReadPos; \ + } - void sb_ReadSkipWhitespace(strBufP); - void sb_ReadSkipInteger(strBufP); -#define sb_ReadSkipChar(theStrBuf) \ - { \ - theStrBuf->readPos++; \ - } +void sb_ReadSkipWhitespace(strBufP); +void sb_ReadSkipInteger(strBufP); +#define sb_ReadSkipChar(theStrBuf) \ + { \ + theStrBuf->readPos++; \ + } - int sb_ConcatString(strBufP, char *); - int sb_ConcatChar(strBufP, char); +int sb_ConcatString(strBufP, char const *); +int sb_ConcatChar(strBufP, char); - char *sb_TakeString(strBufP); +char * sb_TakeString(strBufP); #ifndef SPEED_MACROS -// Optimized SPEED_MACROS versions of larger methods are not used in this module +// Optimized SPEED_MACROS versions of larger methods are not used in this +// module #else -// Optimized SPEED_MACROS versions of larger methods are not used in this module +// Optimized SPEED_MACROS versions of larger methods are not used in this +// module #endif #ifdef __cplusplus diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c index 8902c6876..93e0a681d 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.c @@ -33,7 +33,7 @@ void Message(char *message) } } -void ErrorMessage(char *message) +void ErrorMessage(char const*message) { if (!getQuietModeSetting()) { @@ -44,10 +44,11 @@ void ErrorMessage(char *message) int GetNumCharsToReprInt(int theNum, int *numCharsRequired) { + int charCount; if (numCharsRequired == NULL) return NOTOK; - - int charCount = 0; + // mixing declarations and code is incompatible with standards before C99 + charCount = 0; if (theNum < 0) { diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h index 7ac8ae394..a52739a24 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/lowLevelUtils/apiutils.h @@ -30,11 +30,11 @@ extern "C" extern void setQuietModeSetting(int); extern void Message(char *message); - extern void ErrorMessage(char *message); + extern void ErrorMessage(char const*message); int GetNumCharsToReprInt(int theNum, int *numCharsRequired); #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c index a437e4f6f..71f16ec19 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphDrawPlanar.c @@ -596,7 +596,7 @@ int _ComputeEdgeRanges(DrawPlanarContext *context) Uses the extFace links to traverse to the next vertex on the external face given a current vertex and the link that points to its predecessor. ********************************************************************/ -int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink) +static int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink) { int nextVertex = gp_GetExtFaceVertex(theGraph, curVertex, 1 ^ *pPrevLink); @@ -785,7 +785,7 @@ int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink) representation otherwise. The string can be printed using %s, ********************************************************************/ -char *_RenderToString(graphP theEmbedding) +static char *_RenderToString(graphP theEmbedding) { DrawPlanarContext *context = NULL; gp_FindExtension(theEmbedding, DRAWPLANAR_ID, (void *)&context); diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c index 54362217d..7d63b2e4f 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphEmbed.c @@ -38,6 +38,11 @@ int _OrientVerticesInEmbedding(graphP theGraph); int _OrientVerticesInBicomp(graphP theGraph, int BicompRoot, int PreserveSigns); int _JoinBicomps(graphP theGraph); +// Exported function prototypes + +int _HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink); +int _OrientExternalFacePath(graphP theGraph, int u, int v, int w, int x); + /******************************************************************** gp_Embed() diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c index 4ed8ee723..3030edd45 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphIsolator.c @@ -51,6 +51,8 @@ int _GetLeastAncestorConnection(graphP theGraph, int cutVertex); int _MarkDFSPathsToDescendants(graphP theGraph); int _AddAndMarkUnembeddedEdges(graphP theGraph); +int _MarkDFSPath(graphP theGraph, int ancestor, int descendant); + /**************************************************************************** gp_IsolateKuratowskiSubgraph() ****************************************************************************/ diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c index 91707fe4d..63013e742 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphNonplanar.c @@ -35,6 +35,7 @@ int _MarkHighestXYPath(graphP theGraph); int _MarkClosestXYPath(graphP theGraph, int targetVertex); int _MarkZtoRPath(graphP theGraph); int _FindFuturePertinenceBelowXYPath(graphP theGraph); +int _MarkLowestXYPath(graphP theGraph); /**************************************************************************** _ChooseTypeOfNonplanarityMinor() diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c index 36920391b..94e8a0d2f 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/planarityRelated/graphTests.c @@ -404,7 +404,7 @@ int _CheckObstructionIntegrity(graphP theGraph, graphP origGraph) Return OK otherwise. ********************************************************************/ -int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, +static int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, int *imageVerts, int maxNumImageVerts) { int K, v, imageVertPos, degree; @@ -458,7 +458,7 @@ int _getImageVertices(graphP theGraph, int *degrees, int maxDegree, TRUE otherwise ********************************************************************/ -int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, +static int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, int *degrees, int *imageVerts) { int v, w; @@ -517,7 +517,7 @@ int _TestForCompleteGraphObstruction(graphP theGraph, int numVerts, returns TRUE if so, FALSE if not ********************************************************************/ -int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) +static int _TestForK33GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) { int v, K, imageVertPos, temp, success; @@ -641,7 +641,7 @@ int _CheckKuratowskiSubgraphIntegrity(graphP theGraph) returns TRUE if so, FALSE if not ********************************************************************/ -int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) +static int _TestForK23GraphObstruction(graphP theGraph, int *degrees, int *imageVerts) { int v, e, imageVertPos; diff --git a/m4/ax_check_planarity.m4 b/m4/ax_check_planarity.m4 index 2d44c9068..3d258a37f 100644 --- a/m4/ax_check_planarity.m4 +++ b/m4/ax_check_planarity.m4 @@ -14,21 +14,21 @@ AC_DEFUN([AX_CHECK_PLANARITY], [ AC_MSG_RESULT([$with_external_planarity]) if test "x$with_external_planarity" = xyes ; then AC_LANG_PUSH([C]) - AC_CHECK_LIB([planarity], - [gp_InitGraph], + AC_CHECK_LIB([planarity], + [gp_InitGraph], [], [AC_MSG_ERROR([no external libplanarity found])]) - AC_CHECK_HEADER([planarity/graph.h], - [], - [AC_MSG_ERROR([no external planarity headers found])]) + AC_CHECK_HEADER([planarity/graph.h], + [], + [AC_MSG_WARN([no external planarity headers found])]) AC_LANG_POP() fi if test "x$with_external_planarity" = xno ; then WITH_INCLUDED_PLANARITY=yes AC_SUBST(WITH_INCLUDED_PLANARITY) - AC_DEFINE([WITH_INCLUDED_PLANARITY], - [1], + AC_DEFINE([WITH_INCLUDED_PLANARITY], + [1], [define that we should use the vendored planarity]) fi ]) diff --git a/src/planar.c b/src/planar.c index 18ddb1d2b..8b5291fa2 100644 --- a/src/planar.c +++ b/src/planar.c @@ -12,19 +12,19 @@ #include "planar.h" // C headers -#include // for INT_MAX -#include // for true and false +#include // for INT_MAX +#include // for true and false // Digraphs package headers -#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY -#include "digraphs-debug.h" // for DIGRAPHS_ASSERT -#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . -#include "gap-includes.h" // for Fail, False, True +#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . +#include "gap-includes.h" // for Fail, False, True // edge-addition-planarity-suite headers #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored \ +#pragma clang diagnostic ignored \ "-Wincompatible-pointer-types-discards-qualifiers" #pragma clang diagnostic ignored "-Wstrict-prototypes" #elif defined(__GNUC__) @@ -35,7 +35,11 @@ #ifdef DIGRAPHS_WITH_INCLUDED_PLANARITY #include "c/graphLib.h" #else +#ifdef GP_PROJECTVERSION_MAJOR #include "planarity/graphLib.h" +#else + +#endif #endif #if defined(__clang__) #pragma clang diagnostic pop @@ -50,224 +54,237 @@ Obj trivial_planarity_output(Int V, bool krtwsk); Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk); // GAP level functions -Obj FuncIS_PLANAR(Obj self, Obj digraph) { - return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); +Obj FuncIS_PLANAR(Obj self, Obj digraph) +{ + return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); } -Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) { - return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); +Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) +{ + return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); } -Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) { - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) +{ + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } // The implementation of the main functions in this file. -Obj trivial_planarity_output(Int V, bool krtwsk) { - Obj res; - if (krtwsk) { - Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); - SET_LEN_PLIST(subgraph, V); - for (int i = 1; i <= V; ++i) { - Obj list = NEW_PLIST_IMM(T_PLIST, 0); - SET_LEN_PLIST(list, 0); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); +Obj trivial_planarity_output(Int V, bool krtwsk) +{ + Obj res; + if (krtwsk) { + Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); + SET_LEN_PLIST(subgraph, V); + for (int i = 1; i <= V; ++i) { + Obj list = NEW_PLIST_IMM(T_PLIST, 0); + SET_LEN_PLIST(list, 0); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); + } + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, True); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } + else { + res = True; } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, True); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } else { - res = True; - } - return res; + return res; } // This function only accepts digraphs without multiple edges -Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { - DIGRAPHS_ASSERT(flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR - || flags == EMBEDFLAGS_SEARCHFORK23 - || flags == EMBEDFLAGS_SEARCHFORK4 - || flags == EMBEDFLAGS_SEARCHFORK33); +Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) +{ + DIGRAPHS_ASSERT( + flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR || + flags == EMBEDFLAGS_SEARCHFORK23 || flags == EMBEDFLAGS_SEARCHFORK4 || + flags == EMBEDFLAGS_SEARCHFORK33); - if (CALL_1ARGS(IsDigraph, digraph) != True) { - ErrorQuit("Digraphs: boyers_planarity_check (C): the 1st argument must be " - "a digraph, not %s", - (Int) TNAM_OBJ(digraph), - 0L); - } - if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { - ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); - } + if (CALL_1ARGS(IsDigraph, digraph) != True) { + ErrorQuit( + "Digraphs: boyers_planarity_check (C): the 1st argument must be " + "a digraph, not %s", + (Int)TNAM_OBJ(digraph), 0L); + } + if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { + ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); + } - Int V = DigraphNrVertices(digraph); - if (V == 0) { - return trivial_planarity_output(0, krtwsk); - } + Int V = DigraphNrVertices(digraph); + if (V == 0) { + return trivial_planarity_output(0, krtwsk); + } - Int E = DigraphNrAdjacenciesWithoutLoops(digraph); - if (E == 0) { - return trivial_planarity_output(V, krtwsk); - } - if (V > INT_MAX) { - // Cannot currently test this, it might always be true, depending on - // the definition of Int. - ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " - "nodes is %d, found %d", - INT_MAX, - V); - return 0L; - } else if (2 * E > INT_MAX) { - // Cannot currently test this - ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " - "edges is %d, found %d", - INT_MAX / 2, - E); - return 0L; - } + Int E = DigraphNrAdjacenciesWithoutLoops(digraph); + if (E == 0) { + return trivial_planarity_output(V, krtwsk); + } + if (V > INT_MAX) { + // Cannot currently test this, it might always be true, depending on + // the definition of Int. + ErrorQuit( + "Digraphs: boyers_planarity_check (C): the maximum number of " + "nodes is %d, found %d", + INT_MAX, V); + return 0L; + } + else if (2 * E > INT_MAX) { + // Cannot currently test this + ErrorQuit( + "Digraphs: boyers_planarity_check (C): the maximum number of " + "edges is %d, found %d", + INT_MAX / 2, E); + return 0L; + } - graphP theGraph = gp_New(); + graphP theGraph = gp_New(); - if (gp_InitGraph(theGraph, V) != OK) { - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", - 0L, - 0L); - return 0L; - } else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of edges!", - 0L, - 0L); - return 0L; - } + if (gp_InitGraph(theGraph, V) != OK) { + gp_Free(&theGraph); + ErrorQuit( + "Digraphs: boyers_planarity_check (C): invalid number of nodes!", + 0L, 0L); + return 0L; + } + else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { + gp_Free(&theGraph); + ErrorQuit( + "Digraphs: boyers_planarity_check (C): invalid number of edges!", + 0L, 0L); + return 0L; + } - switch (flags) { + switch (flags) { case EMBEDFLAGS_SEARCHFORK33: - gp_AttachK33Search(theGraph); - break; + gp_AttachK33Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK23: - gp_AttachK23Search(theGraph); - break; + gp_AttachK23Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK4: - gp_AttachK4Search(theGraph); - break; + gp_AttachK4Search(theGraph); + break; default: - break; - } + break; + } - int status; - Obj const out = FuncOutNeighbours(0L, digraph); + int status; + Obj const out = FuncOutNeighbours(0L, digraph); - // Construct the antisymmetric digraph with no loops - for (Int v = 1; v <= LEN_LIST(out); ++v) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); - gp_SetVertexIndex(theGraph, v, v); - Obj const out_v = ELM_LIST(out, v); - for (Int w = 1; w <= LEN_LIST(out_v); ++w) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); - Int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v < u - || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) - == False) { - status = gp_AddEdge(theGraph, v, 0, u, 0); - if (status != OK) { - // Cannot currently test this, i.e. it shouldn't happen - // (and currently there is no example where it does - // happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): " - "internal error, " - "can't add edge from %d to %d", - (Int) v, - (Int) u); - return 0L; + // Construct the antisymmetric digraph with no loops + for (Int v = 1; v <= LEN_LIST(out); ++v) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); + gp_SetVertexIndex(theGraph, v, v); + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), + INTOBJ_INT(v)) == False) { + status = gp_AddEdge(theGraph, v, 0, u, 0); + if (status != OK) { + // Cannot currently test this, i.e. it shouldn't happen + // (and currently there is no example where it does + // happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): " + "internal error, " + "can't add edge from %d to %d", + (Int)v, (Int)u); + return 0L; + } + } } - } } - } - status = gp_Embed(theGraph, flags); - if (status == NOTOK) { - // Cannot currently test this, i.e. it shouldn't happen (and - // currently there is no example where it does happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", 0L, 0L); - } + status = gp_Embed(theGraph, flags); + if (status == NOTOK) { + // Cannot currently test this, i.e. it shouldn't happen (and + // currently there is no example where it does happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", + 0L, 0L); + } - // Construct the return value - Obj res; - if (krtwsk) { - // Kuratowski subgraph isolator - gp_SortVertices(theGraph); - Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); - SET_LEN_PLIST(subgraph, theGraph->N); - for (int i = 1; i <= theGraph->N; ++i) { - int nr = 0; - Obj list = NEW_PLIST(T_PLIST, 0); - int j = theGraph->V[i].link[1]; - while (j) { - if (CALL_3ARGS(IsDigraphEdge, - digraph, - INTOBJ_INT((Int) i), - INTOBJ_INT((Int) theGraph->E[j].neighbor)) - == True) { - AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); + // Construct the return value + Obj res; + if (krtwsk) { + // Kuratowski subgraph isolator + gp_SortVertices(theGraph); + Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); + SET_LEN_PLIST(subgraph, theGraph->N); + for (int i = 1; i <= theGraph->N; ++i) { + int nr = 0; + Obj list = NEW_PLIST(T_PLIST, 0); + int j = theGraph->V[i].link[1]; + while (j) { + if (CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT((Int)i), + INTOBJ_INT((Int)theGraph->E[j].neighbor)) == + True) { + AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); + } + j = theGraph->E[j].link[1]; + } + if (nr == 0) { + SET_LEN_PLIST(list, 0); + } + MakeImmutable(list); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); } - j = theGraph->E[j].link[1]; - } - if (nr == 0) { - SET_LEN_PLIST(list, 0); - } - MakeImmutable(list); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } + else if (status == NONEMBEDDABLE) { + res = False; } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } else if (status == NONEMBEDDABLE) { - res = False; - } else { - res = True; - } - gp_Free(&theGraph); - return res; + else { + res = True; + } + gp_Free(&theGraph); + return res; } From 9901983d5ab9849bb209885e2fae6f795214db53 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Tue, 8 Apr 2025 16:30:35 +0200 Subject: [PATCH 6/7] Suppress all remaining compiler warnings --- .../c/graphLib/io/g6-read-iterator.c | 16 + .../c/graphLib/io/g6-write-iterator.c | 14 + .../c/graphLib/io/graphIO.c | 16 + .../c/graphLib/io/strOrFile.c | 15 + src/planar.c | 381 +++++++++--------- 5 files changed, 245 insertions(+), 197 deletions(-) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c index 3c1744815..5b18240c0 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c @@ -7,6 +7,16 @@ See the LICENSE.TXT file for licensing information. #include #include +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeclaration-after-statement" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#endif + #include "g6-read-iterator.h" #include "g6-api-utilities.h" @@ -727,3 +737,9 @@ int _ReadGraphFromG6StrOrFile(graphP pGraphToRead, strOrFileP g6InputContainer) return exitCode; } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c index 4f8fa9bf1..f2d029381 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c @@ -7,6 +7,15 @@ See the LICENSE.TXT file for licensing information. #include #include +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeclaration-after-statement" +#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#endif + #include "g6-write-iterator.h" #include "g6-api-utilities.h" @@ -565,3 +574,8 @@ int _WriteGraphToG6StrOrFile(graphP pGraph, strOrFileP outputContainer, char **o return exitCode; } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c index 510529916..67029af14 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c @@ -8,6 +8,16 @@ See the LICENSE.TXT file for licensing information. #include #include +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" +#pragma clang diagnostic ignored "-Wdeclaration-after-statement" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#endif + #include "../graph.h" /* Private functions (exported to system) */ @@ -1065,3 +1075,9 @@ char *_MakeLogStr5(char *format, int one, int two, int three, int four, int five sprintf(LogStr, format, one, two, three, four, five); return LogStr; } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c index c0f141c90..4e854df3b 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c @@ -10,6 +10,15 @@ See the LICENSE.TXT file for licensing information. #include #include +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeclaration-after-statement" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#endif + + #include "../lowLevelUtils/appconst.h" #include "strOrFile.h" @@ -716,3 +725,9 @@ void sf_Free(strOrFileP *pStrOrFile) (*pStrOrFile) = NULL; } } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/src/planar.c b/src/planar.c index 8b5291fa2..a5b8fbd64 100644 --- a/src/planar.c +++ b/src/planar.c @@ -12,19 +12,19 @@ #include "planar.h" // C headers -#include // for INT_MAX -#include // for true and false +#include // for INT_MAX +#include // for true and false // Digraphs package headers -#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY -#include "digraphs-debug.h" // for DIGRAPHS_ASSERT -#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . -#include "gap-includes.h" // for Fail, False, True +#include "digraphs-config.h" // for DIGRAPHS_WITH_INCLUDED_PLANARITY +#include "digraphs-debug.h" // for DIGRAPHS_ASSERT +#include "digraphs.h" // for DigraphNrVertices, DigraphNrEdges, . . . +#include "gap-includes.h" // for Fail, False, True // edge-addition-planarity-suite headers #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored \ +#pragma clang diagnostic ignored \ "-Wincompatible-pointer-types-discards-qualifiers" #pragma clang diagnostic ignored "-Wstrict-prototypes" #elif defined(__GNUC__) @@ -54,237 +54,224 @@ Obj trivial_planarity_output(Int V, bool krtwsk); Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk); // GAP level functions -Obj FuncIS_PLANAR(Obj self, Obj digraph) -{ - return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); +Obj FuncIS_PLANAR(Obj self, Obj digraph) { + return boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, false); } -Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_PLANAR_SUBGRAPH(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncPLANAR_EMBEDDING(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_PLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) -{ - return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); +Obj FuncIS_OUTER_PLANAR(Obj self, Obj digraph) { + return boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, false); } -Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncKURATOWSKI_OUTER_PLANAR_SUBGRAPH(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); - return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); +Obj FuncOUTER_PLANAR_EMBEDDING(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_OUTERPLANAR, true); + return (ELM_PLIST(res, 1) == True ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K23(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK23, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K33(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK33, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } -Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) -{ - Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); - return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); +Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) { + Obj res = boyers_planarity_check(digraph, EMBEDFLAGS_SEARCHFORK4, true); + return (ELM_PLIST(res, 1) == False ? ELM_PLIST(res, 2) : Fail); } // The implementation of the main functions in this file. -Obj trivial_planarity_output(Int V, bool krtwsk) -{ - Obj res; - if (krtwsk) { - Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); - SET_LEN_PLIST(subgraph, V); - for (int i = 1; i <= V; ++i) { - Obj list = NEW_PLIST_IMM(T_PLIST, 0); - SET_LEN_PLIST(list, 0); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); - } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, True); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } - else { - res = True; +Obj trivial_planarity_output(Int V, bool krtwsk) { + Obj res; + if (krtwsk) { + Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); + SET_LEN_PLIST(subgraph, V); + for (int i = 1; i <= V; ++i) { + Obj list = NEW_PLIST_IMM(T_PLIST, 0); + SET_LEN_PLIST(list, 0); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); } - return res; + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, True); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } else { + res = True; + } + return res; } // This function only accepts digraphs without multiple edges -Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) -{ - DIGRAPHS_ASSERT( - flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR || - flags == EMBEDFLAGS_SEARCHFORK23 || flags == EMBEDFLAGS_SEARCHFORK4 || - flags == EMBEDFLAGS_SEARCHFORK33); +Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { + DIGRAPHS_ASSERT(flags == EMBEDFLAGS_PLANAR || flags == EMBEDFLAGS_OUTERPLANAR + || flags == EMBEDFLAGS_SEARCHFORK23 + || flags == EMBEDFLAGS_SEARCHFORK4 + || flags == EMBEDFLAGS_SEARCHFORK33); - if (CALL_1ARGS(IsDigraph, digraph) != True) { - ErrorQuit( - "Digraphs: boyers_planarity_check (C): the 1st argument must be " - "a digraph, not %s", - (Int)TNAM_OBJ(digraph), 0L); - } - if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { - ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); - } + if (CALL_1ARGS(IsDigraph, digraph) != True) { + ErrorQuit("Digraphs: boyers_planarity_check (C): the 1st argument must be " + "a digraph, not %s", + (Int) TNAM_OBJ(digraph), + 0L); + } + if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { + ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); + } - Int V = DigraphNrVertices(digraph); - if (V == 0) { - return trivial_planarity_output(0, krtwsk); - } + Int V = DigraphNrVertices(digraph); + if (V == 0) { + return trivial_planarity_output(0, krtwsk); + } - Int E = DigraphNrAdjacenciesWithoutLoops(digraph); - if (E == 0) { - return trivial_planarity_output(V, krtwsk); - } - if (V > INT_MAX) { - // Cannot currently test this, it might always be true, depending on - // the definition of Int. - ErrorQuit( - "Digraphs: boyers_planarity_check (C): the maximum number of " - "nodes is %d, found %d", - INT_MAX, V); - return 0L; - } - else if (2 * E > INT_MAX) { - // Cannot currently test this - ErrorQuit( - "Digraphs: boyers_planarity_check (C): the maximum number of " - "edges is %d, found %d", - INT_MAX / 2, E); - return 0L; - } + Int E = DigraphNrAdjacenciesWithoutLoops(digraph); + if (E == 0) { + return trivial_planarity_output(V, krtwsk); + } + if (V > INT_MAX) { + // Cannot currently test this, it might always be true, depending on + // the definition of Int. + ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " + "nodes is %d, found %d", + INT_MAX, + V); + return 0L; + } else if (2 * E > INT_MAX) { + // Cannot currently test this + ErrorQuit("Digraphs: boyers_planarity_check (C): the maximum number of " + "edges is %d, found %d", + INT_MAX / 2, + E); + return 0L; + } - graphP theGraph = gp_New(); + graphP theGraph = gp_New(); - if (gp_InitGraph(theGraph, V) != OK) { - gp_Free(&theGraph); - ErrorQuit( - "Digraphs: boyers_planarity_check (C): invalid number of nodes!", - 0L, 0L); - return 0L; - } - else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { - gp_Free(&theGraph); - ErrorQuit( - "Digraphs: boyers_planarity_check (C): invalid number of edges!", - 0L, 0L); - return 0L; - } + if (gp_InitGraph(theGraph, V) != OK) { + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", + 0L, + 0L); + return 0L; + } else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of edges!", + 0L, + 0L); + return 0L; + } - switch (flags) { + switch (flags) { case EMBEDFLAGS_SEARCHFORK33: - gp_AttachK33Search(theGraph); - break; + gp_AttachK33Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK23: - gp_AttachK23Search(theGraph); - break; + gp_AttachK23Search(theGraph); + break; case EMBEDFLAGS_SEARCHFORK4: - gp_AttachK4Search(theGraph); - break; + gp_AttachK4Search(theGraph); + break; default: - break; - } + break; + } - int status; - Obj const out = FuncOutNeighbours(0L, digraph); + int status; + Obj const out = FuncOutNeighbours(0L, digraph); - // Construct the antisymmetric digraph with no loops - for (Int v = 1; v <= LEN_LIST(out); ++v) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); - gp_SetVertexIndex(theGraph, v, v); - Obj const out_v = ELM_LIST(out, v); - for (Int w = 1; w <= LEN_LIST(out_v); ++w) { - DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); - Int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v < u || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), - INTOBJ_INT(v)) == False) { - status = gp_AddEdge(theGraph, v, 0, u, 0); - if (status != OK) { - // Cannot currently test this, i.e. it shouldn't happen - // (and currently there is no example where it does - // happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): " - "internal error, " - "can't add edge from %d to %d", - (Int)v, (Int)u); - return 0L; - } - } + // Construct the antisymmetric digraph with no loops + for (Int v = 1; v <= LEN_LIST(out); ++v) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); + gp_SetVertexIndex(theGraph, v, v); + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u + || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) + == False) { + status = gp_AddEdge(theGraph, v, 0, u, 0); + if (status != OK) { + // Cannot currently test this, i.e. it shouldn't happen + // (and currently there is no example where it does + // happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): " + "internal error, " + "can't add edge from %d to %d", + (Int) v, + (Int) u); + return 0L; } + } } + } - status = gp_Embed(theGraph, flags); - if (status == NOTOK) { - // Cannot currently test this, i.e. it shouldn't happen (and - // currently there is no example where it does happen) - gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", - 0L, 0L); - } + status = gp_Embed(theGraph, flags); + if (status == NOTOK) { + // Cannot currently test this, i.e. it shouldn't happen (and + // currently there is no example where it does happen) + gp_Free(&theGraph); + ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", 0L, 0L); + } - // Construct the return value - Obj res; - if (krtwsk) { - // Kuratowski subgraph isolator - gp_SortVertices(theGraph); - Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); - SET_LEN_PLIST(subgraph, theGraph->N); - for (int i = 1; i <= theGraph->N; ++i) { - int nr = 0; - Obj list = NEW_PLIST(T_PLIST, 0); - int j = theGraph->V[i].link[1]; - while (j) { - if (CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT((Int)i), - INTOBJ_INT((Int)theGraph->E[j].neighbor)) == - True) { - AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); - } - j = theGraph->E[j].link[1]; - } - if (nr == 0) { - SET_LEN_PLIST(list, 0); - } - MakeImmutable(list); - SET_ELM_PLIST(subgraph, i, list); - CHANGED_BAG(subgraph); + // Construct the return value + Obj res; + if (krtwsk) { + // Kuratowski subgraph isolator + gp_SortVertices(theGraph); + Obj subgraph = NEW_PLIST_IMM(T_PLIST, theGraph->N); + SET_LEN_PLIST(subgraph, theGraph->N); + for (int i = 1; i <= theGraph->N; ++i) { + int nr = 0; + Obj list = NEW_PLIST(T_PLIST, 0); + int j = theGraph->V[i].link[1]; + while (j) { + if (CALL_3ARGS(IsDigraphEdge, + digraph, + INTOBJ_INT((Int) i), + INTOBJ_INT((Int) theGraph->E[j].neighbor)) + == True) { + AssPlist(list, ++nr, INTOBJ_INT(theGraph->E[j].neighbor)); } - res = NEW_PLIST_IMM(T_PLIST, 2); - SET_LEN_PLIST(res, 2); - SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); - SET_ELM_PLIST(res, 2, subgraph); - CHANGED_BAG(res); - } - else if (status == NONEMBEDDABLE) { - res = False; + j = theGraph->E[j].link[1]; + } + if (nr == 0) { + SET_LEN_PLIST(list, 0); + } + MakeImmutable(list); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); } - else { - res = True; - } - gp_Free(&theGraph); - return res; + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, (status == NONEMBEDDABLE ? False : True)); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } else if (status == NONEMBEDDABLE) { + res = False; + } else { + res = True; + } + gp_Free(&theGraph); + return res; } From 3e2772f9aa49f502503aa6e2cada62f654f03232 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 9 Apr 2025 01:09:20 +0200 Subject: [PATCH 7/7] Suppress some GCC warnings --- .../c/graphLib/extensionSystem/graphExtensions.c | 6 +++--- .../c/graphLib/io/g6-read-iterator.c | 5 ++++- .../c/graphLib/io/g6-write-iterator.c | 3 +++ .../c/graphLib/io/graphIO.c | 4 ++++ .../c/graphLib/io/strOrFile.c | 5 +++-- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c index 883cb7898..4e69f091f 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/extensionSystem/graphExtensions.c @@ -262,7 +262,7 @@ int gp_AddExtension(graphP theGraph, void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions) { - void **graphFunctionTable = (void **)&theGraph->functions; + void **_graphFunctionTable = (void **)&theGraph->functions; void **newFunctionTable = (void **)functions; int numFunctions = sizeof(theGraph->functions) / sizeof(void *); int K; @@ -271,8 +271,8 @@ void _OverloadFunctions(graphP theGraph, graphFunctionTableP functions) { if (newFunctionTable[K] != NULL) { - void *fp = graphFunctionTable[K]; - graphFunctionTable[K] = newFunctionTable[K]; + void *fp = _graphFunctionTable[K]; + _graphFunctionTable[K] = newFunctionTable[K]; newFunctionTable[K] = fp; } } diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c index 5b18240c0..11aa07c5a 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-read-iterator.c @@ -15,6 +15,9 @@ See the LICENSE.TXT file for licensing information. #pragma clang diagnostic ignored "-Wformat-nonliteral" #elif defined(__GNUC__) #pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" +#pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif #include "g6-read-iterator.h" @@ -236,7 +239,7 @@ int _beginG6ReadIteration(G6ReadIterator *pG6ReadIterator) int _processAndCheckHeader(strOrFileP g6Input) { - char const*correctG6Header,*sparse6Header,*digraph6Header; + char const *correctG6Header, *sparse6Header, *digraph6Header; int exitCode = OK; if (g6Input == NULL) diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c index f2d029381..e0674055f 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/g6-write-iterator.c @@ -14,6 +14,9 @@ See the LICENSE.TXT file for licensing information. #pragma clang diagnostic ignored "-Wmissing-prototypes" #elif defined(__GNUC__) #pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" #endif #include "g6-write-iterator.h" diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c index 67029af14..4f25dcd19 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/graphIO.c @@ -16,6 +16,10 @@ See the LICENSE.TXT file for licensing information. #pragma clang diagnostic ignored "-Wdeclaration-after-statement" #elif defined(__GNUC__) #pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" +#pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif #include "../graph.h" diff --git a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c index 4e854df3b..a7b975dc4 100644 --- a/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c +++ b/extern/edge-addition-planarity-suite-Version_4.0.0.0/c/graphLib/io/strOrFile.c @@ -16,9 +16,10 @@ See the LICENSE.TXT file for licensing information. #pragma clang diagnostic ignored "-Wmissing-prototypes" #elif defined(__GNUC__) #pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" +#pragma GCC diagnostic ignored "-Wnull-dereference" #endif - #include "../lowLevelUtils/appconst.h" #include "strOrFile.h" @@ -30,7 +31,7 @@ See the LICENSE.TXT file for licensing information. Returns the allocated string-or-file container, or NULL on error. ********************************************************************/ -strOrFileP sf_New(char const*theStr, char const*fileName, char const*ioMode) +strOrFileP sf_New(char const *theStr, char const *fileName, char const *ioMode) { strOrFileP theStrOrFile; int containerType = 0;