"""Probe which valid parity partitions of the Fig. 2.10 dual T (24-vertex
5-connected triangulation) make it bridge-derived, and what distinguishes
them.

For every valid parity partition L of T we record:
  - e_cross, e_A, e_B and the total first Betti number of the two parity
    subgraphs (which equals (c_A + c_B) - 2, since e_A + e_B = n - 2 for
    every 4-coloring of a triangulation: each triangle has exactly one
    within-colour-pair edge, 2n-4 triangles, each such edge in 2 faces);
  - whether L is the BFS-level parity of T from some source;
  - the outcome of a backward bridge-orbit search (found / capped).

Finding: minimal total Betti (= 1 here, since T is not intertwining) is
necessary for the bridge-derived partitions we locate, but far from
sufficient -- the bridge-derivable partitions form a small, sharply
separated subset distinguished by tiny flip-orbit size, not by any simple
count. See the paper subsection "Toward a characterization of
bridge-derived graphs".
"""
import sys
import os
import time
from collections import defaultdict
sys.path.insert(0, '/Users/didericis/Code/math-research/papers/'
                'level_resolutions_of_maximal_planar_graphs/experiments')
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import networkx as nx
from sage.all import Graph  # type: ignore
from tutte_dual_treecolor import dual_triangulation
from test_tutte_bridge import valid_parity_partitions_via_coloring, search_partition
from test_fig210_dual_bridge import sage_to_nx
from fast_bridge import EdgeCode

HERE = os.path.dirname(os.path.abspath(__file__))


def parity_stats(T, labels):
    A = [v for v in T if labels[v] == 0]
    B = [v for v in T if labels[v] == 1]
    GA, GB = T.subgraph(A), T.subgraph(B)
    cA = nx.number_connected_components(GA)
    cB = nx.number_connected_components(GB)
    bA = GA.number_of_edges() - len(A) + cA
    bB = GB.number_of_edges() - len(B) + cB
    ecross = T.number_of_edges() - GA.number_of_edges() - GB.number_of_edges()
    realiz = any(
        all((nx.single_source_shortest_path_length(T, s)[v] % 2) == labels[v] for v in T)
        or all((nx.single_source_shortest_path_length(T, s)[v] % 2) != labels[v] for v in T)
        for s in T
    )
    return dict(eA=GA.number_of_edges(), eB=GB.number_of_edges(), ecross=ecross,
                betti=bA + bB, cA=cA, cB=cB, realiz=realiz)


def main(cap=12000, time_limit=6.0):
    g6 = open(os.path.join(HERE, 'fig210_dual.g6')).read().strip()
    T, _ = dual_triangulation(sage_to_nx(Graph(g6)))
    n = T.number_of_nodes()
    parts, _ = valid_parity_partitions_via_coloring(T)
    code = EdgeCode(T.nodes())
    code.state0 = code.state_of(T)
    print('T: n=%d, e_cross should be 2n-4=%d; %d valid partitions'
          % (n, 2 * n - 4, len(parts)), flush=True)

    by_betti = defaultdict(lambda: [0, 0])   # betti -> [found, not_found]
    ecross_vals = set()
    t0 = time.time()
    for k, labels in enumerate(parts):
        st = parity_stats(T, labels)
        ecross_vals.add(st['ecross'])
        status, sz, depth = search_partition(code, labels, n, cap, time_limit)
        slot = 0 if status == 'found' else 1
        by_betti[st['betti']][slot] += 1
        if status == 'found':
            print('  found k=%d betti=%d depth=%s orbit=%d realiz=%s'
                  % (k, st['betti'], depth, sz, st['realiz']), flush=True)
        if (k + 1) % 50 == 0:
            print('  ...%d/%d (%.0fs)' % (k + 1, len(parts), time.time() - t0), flush=True)

    print('\ne_cross values over all partitions: %s (constant = identity check)'
          % sorted(ecross_vals), flush=True)
    print('betti -> [bridge-derived, not-found-at-cap=%d]:' % cap, flush=True)
    for b in sorted(by_betti):
        print('  betti=%d : %s' % (b, by_betti[b]), flush=True)
    print('(%.0fs)' % (time.time() - t0), flush=True)


if __name__ == '__main__':
    main()
