"""Search for counterexamples to the strengthened Conjecture 5.5:

   Let H be a cubic plane graph in which every face has length ≥ 5,
   with a proper 3-edge-colouring φ. If K_0 = {a,b}-Kempe cycle and
   K_1 = {a,c}-Kempe cycle share a colour-a edge, then h_φ cannot be
   constant on both V(K_0) and V(K_1).

The graphs H satisfying the face-length-≥-5 hypothesis are exactly
the duals of triangulations of the sphere with minimum degree ≥ 5.
We enumerate these via plantri (through Sage's
`graphs.triangulations(n, minimum_degree=5)`), take the planar dual,
and run the same Heawood-constancy check as
`search_smaller_counterexample.py`.

For each triangulation order n, the dual has 2n - 4 cubic vertices.
The smallest case n = 12 is the icosahedron, whose dual is the
dodecahedron (20 vertices, all faces pentagonal).

Run with:
  sage experiments/search_min_face5_counterexample.py        # default max_n=14
  sage experiments/search_min_face5_counterexample.py 16
  sage experiments/search_min_face5_counterexample.py 16 --all
"""
import sys
import time

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

# Reuse the verification machinery from the cubic-search script.
import os
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
from search_smaller_counterexample import (
    check_graph,
    min_face_length,
)


def planar_dual(G):
    """Build the planar dual of a planar graph G with its embedding
    already set via G.is_planar(set_embedding=True)."""
    faces = G.faces()
    D = Graph(multiedges=False, loops=False)
    n_faces = len(faces)
    D.add_vertices(range(n_faces))
    # Map each (undirected) edge of G to the indices of the two faces
    # that contain it.
    edge_to_faces = {}
    for i, face in enumerate(faces):
        for e in face:
            key = frozenset(e[:2])
            edge_to_faces.setdefault(key, []).append(i)
    for key, fs in edge_to_faces.items():
        if len(fs) == 2 and fs[0] != fs[1]:
            D.add_edge(fs[0], fs[1])
    return D


def main():
    args = [a for a in sys.argv[1:] if not a.startswith('--')]
    max_n = int(args[0]) if args else 14
    flag_all = '--all' in sys.argv[1:]

    print(f"Searching for counterexamples to the face-length-≥-5 form "
          f"of Conjecture 5.5.\n"
          f"Iterating over triangulations with min degree ≥ 5 for "
          f"n_T in [12, {max_n}].\n"
          f"Each dual is cubic with all faces of length ≥ 5; the dual "
          f"has 2n_T - 4 vertices.\n"
          f"{'Continuing past first hit.' if flag_all else 'Stopping at first hit.'}\n")

    first_found = None
    for n_T in range(12, max_n + 1):
        start = time.time()
        count = 0
        found = None
        try:
            gen = graphs.triangulations(n_T, minimum_degree=5)
        except Exception as ex:
            print(f"n_T={n_T:>3}: cannot enumerate ({ex})")
            continue
        for T in gen:
            T.is_planar(set_embedding=True)
            H = planar_dual(T)
            n_H = H.order()
            # Sanity: H should be cubic and planar; faces length ≥ 5.
            if max(H.degree()) != 3 or min(H.degree()) != 3:
                continue
            if not H.is_planar(set_embedding=True):
                continue
            if min_face_length(H) < 5:
                continue
            count += 1
            res = check_graph(H)
            if res is not None:
                found = (T.copy(), H.copy(), res, n_H)
                if not flag_all:
                    break
        elapsed = time.time() - start
        n_H_min = 2 * n_T - 4
        if found is None:
            print(f"n_T={n_T:>3}: checked {count} triangulation duals "
                  f"(each with {n_H_min} cubic vertices), no counterexample "
                  f"[{elapsed:.1f}s]")
        else:
            T, H, (col, K0, K1, h0, h1, a, b, c, e), n_H = found
            colour_name = {0: 'red', 1: 'blue', 2: 'green'}
            print(f"n_T={n_T:>3}: COUNTEREXAMPLE in dual #{count} "
                  f"(|V(H)| = {n_H}) [{elapsed:.1f}s]")
            print(f"  triangulation canonical graph6 = "
                  f"{T.canonical_label().graph6_string()}")
            print(f"  dual (cubic) canonical graph6 = "
                  f"{H.canonical_label().graph6_string()}")
            print(f"  dual edges    = {sorted(H.edges(labels=False))}")
            print(f"  colouring     = {col}")
            print(f"  shared colour = {colour_name[a]} ({a}), edge {e}")
            print(f"  K_{{a,b}} = K_{{{colour_name[a]},{colour_name[b]}}} "
                  f"= {K0}  (h={h0:+d}, |V|={len(K0)})")
            print(f"  K_{{a,c}} = K_{{{colour_name[a]},{colour_name[c]}}} "
                  f"= {K1}  (h={h1:+d}, |V|={len(K1)})")
            if first_found is None:
                first_found = n_T
            if not flag_all:
                break
        sys.stdout.flush()

    if first_found is not None:
        print(f"\nSmallest counterexample at triangulation order n_T "
              f"= {first_found} (dual has {2 * first_found - 4} vertices).")
    else:
        print(f"\nNo counterexample found for triangulation orders ≤ "
              f"{max_n} (dual orders ≤ {2 * max_n - 4}).")


if __name__ == '__main__':
    main()
