"""Find a concrete claimed (R1) violation and verify it by hand.

Iterate over small maximal planar graphs, for each single-vertex source
find a depth-d component flagged as having a pinch vertex by the cyclic-
rotation check.  Save the graph + the pinch vertex's rotation, the
depth of each face incident to the pinch vertex, and the cyclic
sequence of "is this face in the component?" booleans.
"""
import sys
from collections import defaultdict

from sage.all import Graph
from sage.graphs.graph_generators import graphs


def face_edges_set(face):
    return frozenset(frozenset(e) for e in face)


def main():
    for n in range(7, 12):
        for G in graphs.triangulations(n):
            G.is_planar(set_embedding=True)
            emb = G.get_embedding()
            faces = G.faces(embedding=emb)
            for v_source in G.vertices():
                dist = G.shortest_path_lengths(v_source)
                face_depths = []
                for face in faces:
                    verts = set()
                    for u, v in face:
                        verts.add(u); verts.add(v)
                    d = min(dist[u] for u in verts)
                    face_depths.append((face, d, verts))
                max_d = max(fd[1] for fd in face_depths)
                for target_d in range(max_d + 1):
                    cands = [(i, fd) for i, fd in enumerate(face_depths)
                             if fd[1] == target_d]
                    if not cands:
                        continue
                    edge_to_idxs = defaultdict(list)
                    for k, (i, fd) in enumerate(cands):
                        for e in [frozenset(ed) for ed in fd[0]]:
                            edge_to_idxs[e].append(k)
                    n_c = len(cands)
                    adj = [[] for _ in range(n_c)]
                    for idxs in edge_to_idxs.values():
                        if len(idxs) >= 2:
                            for a in idxs:
                                for b in idxs:
                                    if a != b:
                                        adj[a].append(b)
                    visited = [False] * n_c
                    comps = []
                    for s in range(n_c):
                        if visited[s]:
                            continue
                        stack = [s]; comp = []
                        while stack:
                            x = stack.pop()
                            if visited[x]: continue
                            visited[x] = True; comp.append(cands[x])
                            for y in adj[x]:
                                if not visited[y]: stack.append(y)
                        comps.append(comp)

                    for comp in comps:
                        comp_fes_set = {face_edges_set(fd[0])
                                        for _, fd in comp}
                        vertices = set()
                        for fes in comp_fes_set:
                            for e in fes:
                                vertices.update(e)
                        for v in vertices:
                            rot = emb[v]
                            in_c = []
                            for i in range(len(rot)):
                                u = rot[i]
                                w = rot[(i + 1) % len(rot)]
                                tri = frozenset({frozenset({v, u}),
                                                  frozenset({u, w}),
                                                  frozenset({v, w})})
                                in_c.append(tri in comp_fes_set)
                            n_true = sum(in_c)
                            if n_true == 0 or n_true == len(in_c):
                                continue
                            n_runs = sum(
                                1 for i in range(len(in_c))
                                if in_c[i] and not in_c[(i - 1) % len(in_c)])
                            if n_runs > 1:
                                # Found one
                                print(f"=== R1 violation candidate ===")
                                print(f"n={n}, source={v_source}, "
                                      f"depth={target_d}, pinch vertex={v}")
                                print(f"edges of G: "
                                      f"{sorted([tuple(sorted(e)) for e in G.edges(labels=False)])}")
                                print(f"BFS distances: {dict(dist)}")
                                print(f"rotation around v={v}: {rot}")
                                lvls = [dist[u] for u in rot]
                                print(f"  levels:           {lvls}")
                                face_depths_around_v = []
                                for i in range(len(rot)):
                                    u = rot[i]
                                    w = rot[(i + 1) % len(rot)]
                                    d_face = min(dist[v], dist[u], dist[w])
                                    face_depths_around_v.append(d_face)
                                print(f"  face depths:      {face_depths_around_v}")
                                print(f"  in_component:     {in_c}")
                                print(f"  n_runs of True:   {n_runs}")
                                # Also list the depth-d faces at v
                                d_target_at_v = [
                                    (rot[i], rot[(i + 1) % len(rot)])
                                    for i in range(len(rot))
                                    if face_depths_around_v[i] == target_d
                                ]
                                print(f"  depth-{target_d} faces at v: "
                                      f"{d_target_at_v}")
                                return


if __name__ == '__main__':
    main()
