"""Test the face-monochromatic-pair conjecture (both the weak form,
clauses 1-3, and the strengthening, clauses 1-4) on the 6 duals of the
non-Hamiltonian 38-vertex cubic plane graphs found by Holton & McKay.

Each Holton-McKay graph is itself a cubic plane graph $G'$; its dual is a
21-vertex triangulation $G$. We treat each Holton-McKay graph as the $G'$
of a hypothetical minimal counterexample and apply the reduced-dual
construction at each of its pentagonal faces.

Reuses the testing infrastructure from check_conj_final_scaled.py.

Run with: sage experiments/check_conj_on_holton_mckay.py
"""
import os
import sys

from sage.all import Graph

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)

from check_conj_final_scaled import (
    apply_reduction,
    proper_3_edge_colorings,
    matches_chord_apex_kempe,
    find_all_36_witnesses,
    check_clause_4,
)
from check_conj_face_kempe_scaled import conjecture_holds_for


HM_FILE = ('/Users/didericis/Code/math-research/papers/'
           'even_level_graph_generators/experiments/nonham38m4.pc')


def parse_planar_code_to_sage(path):
    """Parse McKay's planar_code file and return list of Sage Graphs (each
    a 3-connected cubic plane graph)."""
    with open(path, 'rb') as f:
        data = f.read()
    header = b'>>planar_code<<'
    assert data.startswith(header), data[:20]
    pos = len(header)
    sage_graphs = []
    while pos < len(data):
        n = data[pos]; pos += 1
        edges = set()
        for v in range(n):
            while True:
                w = data[pos]; pos += 1
                if w == 0:
                    break
                edges.add(frozenset((v, w - 1)))
        G = Graph([tuple(e) for e in edges], multiedges=False, loops=False)
        G.is_planar(set_embedding=True)
        sage_graphs.append(G)
    return sage_graphs


def test_one(D, name=""):
    """Test conjecture on one cubic plane graph $D$ ($= G'$). Returns
    (n_cand, n_sat_123, n_sat_1234, faces_used, indices_used)."""
    D.is_planar(set_embedding=True)
    n_cand = n_sat_123 = n_sat_1234 = 0
    face_count = 0
    for face in D.faces():
        if len(face) != 5:
            continue
        face_count += 1
        for i_red in range(5):
            res = apply_reduction(D, face, i_red, 9999)
            if res is None:
                continue
            H = res['H']
            named = res['named']
            H.is_planar(set_embedding=True)
            edges, colorings = proper_3_edge_colorings(H)
            cand = [c for c in colorings
                    if matches_chord_apex_kempe(edges, c, named)]
            for col in cand:
                n_cand += 1
                if conjecture_holds_for(H, edges, col, named):
                    n_sat_123 += 1
                # clauses 1-4: existential at the witness level
                witnesses = find_all_36_witnesses(H, edges, col, named)
                if any(check_clause_4(H, edges, col, named, w)
                       for w in witnesses):
                    n_sat_1234 += 1
    return n_cand, n_sat_123, n_sat_1234, face_count


def main():
    graphs_list = parse_planar_code_to_sage(HM_FILE)
    print(f"Loaded {len(graphs_list)} Holton-McKay graphs from "
          f"{HM_FILE}\n")

    grand_cand = grand_123 = grand_1234 = 0
    rows = []
    import time
    for i, G in enumerate(graphs_list):
        n = G.order()
        m = G.size()
        degs = sorted(set(G.degree()))
        t0 = time.time()
        n_cand, n_123, n_1234, n_pent = test_one(G, name=f"HM{i}")
        dt = time.time() - t0
        rows.append((i, n, m, degs, n_pent, n_cand, n_123, n_1234, dt))
        grand_cand += n_cand
        grand_123 += n_123
        grand_1234 += n_1234
        sys_stdout = sys.stdout
        print(f"HM{i}: n={n} m={m} deg={degs} pent_faces={n_pent} "
              f"cand={n_cand} sat(1-3)={n_123} sat(1-4)={n_1234} "
              f"[{dt:.1f}s]")
        sys.stdout.flush()

    print()
    print("=" * 78)
    print(f"{'i':>2} {'n':>3} {'m':>4} {'#pent':>6} {'#cand':>8} "
          f"{'#sat(1-3)':>10} {'#sat(1-4)':>10} {'time':>7}")
    print("-" * 78)
    for i, n, m, degs, n_pent, n_cand, n_123, n_1234, dt in rows:
        print(f"{i:>2} {n:>3} {m:>4} {n_pent:>6} {n_cand:>8} "
              f"{n_123:>10} {n_1234:>10} {dt:>6.1f}s")
    print("-" * 78)
    print(f"{'tot':>2} {'-':>3} {'-':>4} {'-':>6} {grand_cand:>8} "
          f"{grand_123:>10} {grand_1234:>10}")


if __name__ == '__main__':
    main()
