/*
Copyright (c) 1997-2026, John M. Boyer
All rights reserved.
See the LICENSE.TXT file for licensing information.
*/

#include "graphK33Search.h"
#include "graphK33Search.private.h"

/* Imported functions */

// extern void _ClearAllVisitedFlagsInGraph(graphP);
extern int _ClearAllVisitedFlagsInBicomp(graphP theGraph, int BicompRoot);
extern int _ClearAllVisitedFlagsInOtherBicomps(graphP theGraph, int BicompRoot);
extern void _ClearEdgeVisitedFlagsInUnembeddedEdges(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 _ComputeEdgeRecordType(graphP theGraph, int a, int b, int edgeType);
extern int _RestoreEdgeType(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 _ClearAllVisitedFlagsOnPath(graphP theGraph, int u, int v, int w, int x);
extern int _SetAllVisitedFlagsOnPath(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);

#ifndef USE_MERGEBLOCKER
extern int _IsolateMinorE2(graphP theGraph);
#endif

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 _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 _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 _MarkStraddlingBridgePath(graphP theGraph, int u_min, int u_max, int u_d, int d);

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 = theGraphIC(theGraph);
    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 (theGraphIC(theGraph)->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 (theGraphIC(theGraph)->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, gp_GetN(theGraph)) != 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 = theGraphIC(theGraph);
    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 (_ClearAllVisitedFlagsInBicomp(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) != OK)
        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_GetVisited(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(theGraph, _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(theGraph, _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 = theGraphIC(theGraph)->px, ZPrevLink = 1;

    Z = _GetNeighborOnExtFace(theGraph, Z, &ZPrevLink);

    while (Z != theGraphIC(theGraph)->py)
    {
        if (Z != theGraphIC(theGraph)->w)
        {
            gp_UpdateVertexFuturePertinentChild(theGraph, Z, theGraphIC(theGraph)->v);
            if (FUTUREPERTINENT(theGraph, Z, theGraphIC(theGraph)->v))
            {
                theGraphIC(theGraph)->z = Z;
                theGraphIC(theGraph)->uz = _GetLeastAncestorConnection(theGraph, Z);
                return OK;
            }
            else if (PERTINENT(theGraph, Z))
            {
                /* Swap the roles of W and Z */

                theGraphIC(theGraph)->z = theGraphIC(theGraph)->w;
                theGraphIC(theGraph)->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_GetObstructionMark(theGraph, Z) == ANYVERTEX_OBSTRUCTIONMARK_LOW_RXW)
                    gp_ResetObstructionMark(theGraph, theGraphIC(theGraph)->z, ANYVERTEX_OBSTRUCTIONMARK_LOW_RYW);
                else
                    gp_ResetObstructionMark(theGraph, theGraphIC(theGraph)->z, ANYVERTEX_OBSTRUCTIONMARK_LOW_RXW);

                /* For completeness, we change the new W to type unknown */

                gp_ClearObstructionMark(theGraph, theGraphIC(theGraph)->w);

                /* The external activity ancestor connection of the new Z must be obtained */

                theGraphIC(theGraph)->uz = _GetLeastAncestorConnection(theGraph, theGraphIC(theGraph)->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 = theGraphIC(theGraph);

    /* 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 (_ClearAllVisitedFlagsInOtherBicomps(theGraph, IC->r) != OK)
        return NOTOK;

    /* To complete the normal behavior of _ClearAllVisitedFlagsInGraph() in the
        normal isolator context initialization, we also have to clear
        the visited flags on all edges that have not yet been embedded */

    _ClearEdgeVisitedFlagsInUnembeddedEdges(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(theGraph, 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].backEdgeList;

    while (gp_IsEdge(theGraph, e))
    {
        if (gp_GetNeighbor(theGraph, e) < closerAncestor &&
            gp_GetNeighbor(theGraph, e) > fartherAncestor)
            return gp_GetNeighbor(theGraph, e);

        e = gp_GetNextEdge(theGraph, e);
        if (e == context->VI[theVertex].backEdgeList)
            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 = theGraphIC(theGraph);
    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(theGraph, 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(theGraph, 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(theGraph, 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(theGraph, 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_GetVertexFwdEdgeList(theGraph, ancestor);
    while (gp_IsEdge(theGraph, e))
    {
        if (gp_GetNeighbor(theGraph, e) == cutVertex)
        {
            *pDescendant = cutVertex;
            return OK;
        }

        e = gp_GetNextEdge(theGraph, e);
        if (e == gp_GetVertexFwdEdgeList(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(theGraph, child))
    {
        if (gp_GetVertexLowpoint(theGraph, child) < theGraphIC(theGraph)->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(theGraph, 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 = theGraphIC(theGraph);

    /* 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_GetVertexFromBicompRoot(theGraph, R);

    /* Reset the visitation, pertinence and future pertinence settings from step u_max for step v */

    for (v = gp_LowerBoundVertices(theGraph); v < gp_UpperBoundVertices(theGraph); ++v)
    {
        gp_SetVertexVisitedInfo(theGraph, v, gp_GetN(theGraph));
        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_GetVertexFwdEdgeList(theGraph, IC->v);
    while (gp_IsEdge(theGraph, e))
    {
        theGraph->functions->fpWalkUp(theGraph, IC->v, e);

        e = gp_GetNextEdge(theGraph, e);
        if (e == gp_GetVertexFwdEdgeList(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 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
 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 = theGraphIC(theGraph);
    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_IsNotEdge(theGraph, 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_GetVisited(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_GetFirstEdge(theGraph, v);
        }
        else
            e = gp_GetNextEdge(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_IsEdge(theGraph, 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_GetObstructionMark(theGraph, w) == ANYVERTEX_OBSTRUCTIONMARK_UNMARKED)
            {
                sp_Push2(theGraph->theStack, v, e);
                sp_Push2(theGraph->theStack, w, NIL);

                break;
            }

            e = gp_GetNextEdge(theGraph, e);
        }
    }

    while (!sp_IsEmpty(theGraph->theStack))
    {
        sp_Pop2(theGraph->theStack, v, e);
        gp_SetVisited(theGraph, v);
        gp_SetEdgeVisited(theGraph, e);
        gp_SetEdgeVisited(theGraph, gp_GetTwin(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 = theGraphIC(theGraph);
    int p, c, d, excludedChild, e;

    p = IC->v;
    excludedChild = gp_GetDFSChildFromBicompRoot(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(theGraph, 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_GetFirstEdge(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(theGraph, d))
    {
        c = IC->v;
        while (c != p)
        {
            e = gp_GetFirstEdge(theGraph, c);
            if (gp_IsVertex(theGraph, 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 _ClearAllVisitedFlagsInBicomp(), 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 = theGraphIC(theGraph);
    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_GetLastEdge(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_GetLastEdge(theGraph, IC->x);
            while (B_edge != gp_GetFirstEdge(theGraph, IC->x))
            {
                if (gp_GetEdgeVisited(theGraph, B_edge))
                    break;
                B_edge = gp_GetPrevEdge(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_GetFirstEdge(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_GetFirstEdge(theGraph, IC->r);
        A = gp_GetNeighbor(theGraph, A_edge);
        rxType = EDGE_TYPE_BACK;

        if (max == IC->x)
        {
            B_edge = gp_GetFirstEdge(theGraph, IC->y);
            while (B_edge != gp_GetLastEdge(theGraph, IC->y))
            {
                if (gp_GetEdgeVisited(theGraph, B_edge))
                    break;
                B_edge = gp_GetNextEdge(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_GetLastEdge(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 (_ClearAllVisitedFlagsInBicomp(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_GetTwin(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_GetTwin(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 (_ClearAllVisitedFlagsInBicomp(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)
{
    _K33Search_InitEdgeRec(context, e);
    _K33Search_InitEdgeRec(context, gp_GetTwin(theGraph, e));

    return gp_DeleteEdge(theGraph, e);
}

/********************************************************************
 _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, eNext;
    int stackBottom = sp_GetCurrentSize(theGraph->theStack);

    sp_Push(theGraph->theStack, BicompRoot);
    while (sp_GetCurrentSize(theGraph->theStack) > stackBottom)
    {
        sp_Pop(theGraph->theStack, V);

        e = gp_GetFirstEdge(theGraph, V);
        while (gp_IsEdge(theGraph, e))
        {
            if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_CHILD)
                sp_Push(theGraph->theStack, gp_GetNeighbor(theGraph, e));

            eNext = gp_GetNextEdge(theGraph, e);
            if (!gp_GetEdgeVisited(theGraph, e))
                _K33Search_DeleteEdge(theGraph, context, e);
            e = eNext;
        }
    }
    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_GetFirstEdge(theGraph, u);
    // An edge e is a reduction edge if it has a pathConnector vertex set
    if (context->E[e].pathConnector != NIL)
    {
        if (_RestoreReducedPath(theGraph, context, e) != OK)
            return NOTOK;
        e = gp_GetFirstEdge(theGraph, u);
        v = gp_GetNeighbor(theGraph, e);
    }
    _K33Search_DeleteEdge(theGraph, context, e);

    e = gp_GetLastEdge(theGraph, x);
    // An edge e is a reduction edge if it has a pathConnector vertex set
    if (context->E[e].pathConnector != NIL)
    {
        if (_RestoreReducedPath(theGraph, context, e) != OK)
            return NOTOK;
        e = gp_GetLastEdge(theGraph, x);
        w = gp_GetNeighbor(theGraph, e);
    }
    _K33Search_DeleteEdge(theGraph, context, e);

    /* 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_GetFirstEdge(theGraph, u);
    context->E[e].pathConnector = v;
    gp_SetEdgeType(theGraph, e, _ComputeEdgeRecordType(theGraph, u, x, edgeType));

    e = gp_GetLastEdge(theGraph, x);
    context->E[e].pathConnector = w;
    gp_SetEdgeType(theGraph, e, _ComputeEdgeRecordType(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_GetFirstEdge(theGraph, u);
    e = gp_GetNextEdge(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 */

    // An edge e is a reduction edge if it has a pathConnector vertex set
    if (context->E[e].pathConnector != NIL)
    {
        if (_RestoreReducedPath(theGraph, context, e) != OK)
            return NOTOK;
        e = gp_GetFirstEdge(theGraph, u);
        e = gp_GetNextEdge(theGraph, e);
        v = gp_GetNeighbor(theGraph, e);
    }
    _K33Search_DeleteEdge(theGraph, context, e);

    e = gp_GetFirstEdge(theGraph, x);
    e = gp_GetNextEdge(theGraph, e);
    w = gp_GetNeighbor(theGraph, e);
    // An edge e is a reduction edge if it has a pathConnector vertex set
    if (context->E[e].pathConnector != NIL)
    {
        if (_RestoreReducedPath(theGraph, context, e) != OK)
            return NOTOK;
        e = gp_GetFirstEdge(theGraph, x);
        e = gp_GetNextEdge(theGraph, e);
        w = gp_GetNeighbor(theGraph, e);
    }
    _K33Search_DeleteEdge(theGraph, context, e);

    /* Now add a single edge to represent the XY-path */
    gp_InsertEdge(theGraph, u, gp_GetFirstEdge(theGraph, u), 0,
                  x, gp_GetFirstEdge(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_GetFirstEdge(theGraph, u);
    e = gp_GetNextEdge(theGraph, e);
    context->E[e].pathConnector = v;
    gp_SetEdgeType(theGraph, e, _ComputeEdgeRecordType(theGraph, u, x, edgeType));

    e = gp_GetFirstEdge(theGraph, x);
    e = gp_GetNextEdge(theGraph, e);
    context->E[e].pathConnector = w;
    gp_SetEdgeType(theGraph, e, _ComputeEdgeRecordType(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;

    // Edge e does not represent a reducible path unless it has a
    // pathConnect vertex (i.e., a non-NIL value indicating the vertex)
    if (context->E[e].pathConnector == NIL)
        return OK;

    eTwin = gp_GetTwin(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_GetNextEdge(theGraph, e);
    e1 = gp_GetPrevEdge(theGraph, e);
    eTwin0 = gp_GetNextEdge(theGraph, eTwin);
    eTwin1 = gp_GetPrevEdge(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 edge capacity. */

    _K33Search_DeleteEdge(theGraph, context, e);

    /* 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_IsEdge(theGraph, 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_IsEdge(theGraph, 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 (_RestoreEdgeType(theGraph, v, u) != OK ||
        _RestoreEdgeType(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 edges
 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 e, eTwin, u, v, w, x, visited;
    int e0, eTwin0, e1, eTwin1;

    for (e = gp_LowerBoundEdges(theGraph); e < gp_UpperBoundEdges(theGraph);)
    {
        // An edge e is a reduction edge if it has a pathConnector vertex set
        if (context->E[e].pathConnector != NIL)
        {
            visited = gp_GetEdgeVisited(theGraph, e);

            eTwin = gp_GetTwin(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_GetNextEdge(theGraph, e);
            e1 = gp_GetPrevEdge(theGraph, e);
            eTwin0 = gp_GetNextEdge(theGraph, eTwin);
            eTwin1 = gp_GetPrevEdge(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 edge capacity. */

            _K33Search_DeleteEdge(theGraph, context, e);

            /* 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_IsEdge(theGraph, 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_IsEdge(theGraph, 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 (_RestoreEdgeType(theGraph, u, v) != OK ||
                _RestoreEdgeType(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_IsNotEdge(theGraph, e0) && gp_IsNotEdge(theGraph, eTwin1)) || (gp_IsNotEdge(theGraph, e1) && gp_IsNotEdge(theGraph, 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 (_SetAllVisitedFlagsOnPath(theGraph, u, v, w, x) != OK)
                    return NOTOK;
            }
            else
            {
                if (_ClearAllVisitedFlagsOnPath(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 = theGraphIC(theGraph);
    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_GetVisited(theGraph, p))
    {
        gp_SetVisited(theGraph, p);

        e = gp_GetFirstEdge(theGraph, p);
        while (gp_IsEdge(theGraph, e))
        {
            if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
                break;

            e = gp_GetNextEdge(theGraph, e);
        }

        gp_SetEdgeVisited(theGraph, e);
        gp_SetEdgeVisited(theGraph, gp_GetTwin(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_SetVisited(theGraph, p);
            p = gp_GetVertexFromBicompRoot(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_GetFirstEdge(theGraph, p);
        while (gp_IsEdge(theGraph, e))
        {
            if (gp_GetEdgeType(theGraph, e) == EDGE_TYPE_PARENT)
                break;

            e = gp_GetNextEdge(theGraph, e);
        }

        gp_ClearEdgeVisited(theGraph, e);
        gp_ClearEdgeVisited(theGraph, gp_GetTwin(theGraph, e));

        p = gp_GetNeighbor(theGraph, e);
        gp_ClearVisited(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_GetVertexFromBicompRoot(theGraph, p);
            gp_ClearVisited(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 = theGraphIC(theGraph);

    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 connect 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 = theGraphIC(theGraph);
    int u_min, u_max, d, u_d;

    /* Clear the previously marked x-y path */

    if (_ClearAllVisitedFlagsInBicomp(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 = theGraphIC(theGraph);
    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;
}
