"""At small n (orbits fully exhaustible), classify every triangulation as
bridge-derived / derived / intertwining-tree, and tabulate candidate
invariants on the parity subgraphs to look for one that separates
bridge-derived from the rest.
"""
import sys
import os
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 triangulation_gen import enumerate_all_triangulations
from test_conjecture import canonical_sig, is_even_level_graph
from exhaustive_bridge import valid_parity_partitions
from fast_bridge import EdgeCode
from fast_decide import expand_and_check
from test_disjunction import is_intertwining_tree


def is_bridge_derived(G, cap=2_000_000):
    """Exhaustive: some valid parity partition L has an ELG (parity L) in
    the backward bridge-orbit of G. Feasible only at small n."""
    n = G.number_of_nodes()
    code = EdgeCode(G.nodes())
    code.state0 = code.state_of(G)
    for labels in valid_parity_partitions(G):
        seen = {code.state0}
        frontier = [code.state0]
        while frontier and len(seen) < cap:
            new = []
            for st in frontier:
                wit, neigh = expand_and_check(st, code, labels, n)
                if wit:
                    return True
                for ns in neigh:
                    if ns not in seen:
                        seen.add(ns)
                        new.append(ns)
            frontier = new
    return False


def parity_invariants(G):
    """Over all valid parity partitions, collect (betti_even, betti_odd,
    e_even, e_odd, comps_even, comps_odd) tuples; return the set."""
    out = set()
    for labels in valid_parity_partitions(G):
        ev = [v for v in G.nodes() if labels[v] == 0]
        od = [v for v in G.nodes() if labels[v] == 1]
        SE, SO = G.subgraph(ev), G.subgraph(od)
        be = SE.number_of_edges() - len(ev) + nx.number_connected_components(SE)
        bo = SO.number_of_edges() - len(od) + nx.number_connected_components(SO)
        out.add((be, bo, SE.number_of_edges(), SO.number_of_edges(),
                 nx.number_connected_components(SE),
                 nx.number_connected_components(SO)))
    return out


def main(n):
    tris = enumerate_all_triangulations(n)
    print(f'n={n}: {len(tris)} triangulations', flush=True)
    classes = {'bridge': [], 'not_bridge': []}
    for idx, G in enumerate(tris):
        bd = is_bridge_derived(G)
        it = is_intertwining_tree(G)
        deg = tuple(sorted((d for _, d in G.degree()), reverse=True))
        classes['bridge' if bd else 'not_bridge'].append((idx, it, deg))
    print(f'  bridge-derived: {len(classes["bridge"])}', flush=True)
    print(f'  NOT bridge-derived: {len(classes["not_bridge"])}', flush=True)
    print('  --- NOT bridge-derived (idx, intertwining?, degseq) ---', flush=True)
    for idx, it, deg in classes['not_bridge']:
        print(f'    idx={idx} intertwining={it} deg={deg}', flush=True)


if __name__ == '__main__':
    main(int(sys.argv[1]) if len(sys.argv) > 1 else 9)
