"""Build a 24-vertex L_k where every F'_i (depth-1 neighbour of F) is
lopsided AND each G_i (depth-1 face inside the arm) is also lopsided.
Then iterate preprocessing and see how many steps it takes."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import math
import networkx as nx

n = 24
POS = {i: (math.cos(math.radians(90 - i * 360 / n)),
           math.sin(math.radians(90 - i * 360 / n))) for i in range(n)}
OUTER_EDGES = [(i, (i + 1) % n) for i in range(n)]
outer_set = {frozenset(e) for e in OUTER_EDGES}


def face_edges(f):
    return {frozenset((f[0], f[1])), frozenset((f[1], f[2])),
            frozenset((f[0], f[2]))}


def compute_depths(faces):
    D = nx.Graph()
    D.add_nodes_from(range(len(faces)))
    for i, fi in enumerate(faces):
        for j, fj in enumerate(faces):
            if i < j and face_edges(fi) & face_edges(fj):
                D.add_edge(i, j)
    B = [i for i, f in enumerate(faces)
         if len(face_edges(f) & outer_set) >= 1]
    if not B:
        return {i: float('inf') for i in range(len(faces))}
    return {i: min(nx.shortest_path_length(D, i, b) for b in B)
            for i in range(len(faces))}


U0, U1, U2 = 0, 8, 16

# Per arm (a, b) with b = a + 8:
#   F'_i = (a, a+2, b)
#   E_i  = (a, a+1, a+2)            -- ear
#   G_i  = (a+2, a+4, b)
#   E'_i = (a+2, a+3, a+4)           -- ear
#   K_i  = (a+4, a+6, b)
#   ears (a+4, a+5, a+6) and (a+6, a+7, b)


def arm(a, b):
    chords = [(a, a + 2), (a, b), (a + 2, a + 4), (a + 2, b),
              (a + 4, a + 6), (a + 4, b), (a + 6, b)]
    faces = [
        (a, a + 1, a + 2),
        (a, a + 2, b),       # F'_i (depth 1, lopsided)
        (a + 2, a + 3, a + 4),
        (a + 2, a + 4, b),   # G_i (depth 1, lopsided -- its K_i is depth 1)
        (a + 4, a + 5, a + 6),
        (a + 4, a + 6, b),   # K_i (depth 1, balanced -- both ears depth 0)
        (a + 6, a + 7, b),
    ]
    return chords, faces


CHORDS = [(U0, U1), (U1, U2), (U0, U2)]
FACES = [(U0, U1, U2)]
for (a, b) in [(0, 8), (8, 16), (16, 24 % n)]:
    if b == 0:
        # arm from 16 to 0; treat 0 as the apex
        c, f = arm(a, n)  # use 24 as a placeholder for 0
        # Re-map vertex 24 -> 0
        c = [tuple(0 if v == n else v for v in e) for e in c]
        f = [tuple(0 if v == n else v for v in vt) for vt in f]
    else:
        c, f = arm(a, b)
    CHORDS.extend(c)
    FACES.extend(f)

# Dedup chords (the apex chords (U0,U1), (U1,U2), (U0,U2) get re-added)
CHORDS = list(set(frozenset(c) for c in CHORDS))
CHORDS = [tuple(sorted(c)) for c in CHORDS]

depth = compute_depths(FACES)
print(f'Total faces: {len(FACES)}')
for i, f in enumerate(FACES):
    print(f'  {f} -> depth {depth[i]}')

max_d = max(depth.values())
print(f'\nMax depth: {max_d}')


def check_balanced(F_idx, faces, depth_):
    F = faces[F_idx]
    fe = face_edges(F)
    for e in fe:
        if e in outer_set:
            continue
        cands = [j for j in range(len(faces))
                 if j != F_idx and e in face_edges(faces[j])]
        if not cands:
            continue
        Fp_idx = cands[0]
        if depth_[Fp_idx] != depth_[F_idx] - 1:
            continue
        Fp = faces[Fp_idx]
        fpe = face_edges(Fp)
        d = depth_[F_idx]
        ok = True
        for e2 in fpe:
            if e2 == e:
                continue
            if e2 in outer_set:
                continue
            others = [j for j in range(len(faces))
                      if j != Fp_idx and e2 in face_edges(faces[j])]
            if not others or depth_[others[0]] != d - 2:
                ok = False
                break
        if ok:
            return True, F_idx, Fp_idx, e
    return False, None, None, None


def apply_switch(faces, chords, uv, wx):
    u, v = uv
    w, x = wx
    new_chords = [c for c in chords if set(c) != {u, v}] + \
                 [tuple(sorted((w, x)))]
    new_faces = [f for f in faces
                 if set(f) != {u, v, w} and set(f) != {u, v, x}]
    new_faces.append(tuple(sorted((u, w, x))))
    new_faces.append(tuple(sorted((v, w, x))))
    return new_faces, new_chords


def find_third_vertices(faces, uv):
    u, v = uv
    thirds = []
    for f in faces:
        if u in f and v in f:
            for vert in f:
                if vert not in (u, v):
                    thirds.append(vert)
                    break
    return thirds


# Iteratively preprocess
faces = list(FACES)
chords = list(CHORDS)
print('\nStarting preprocessing loop on the 24-vertex example...')

for step in range(20):
    depth = compute_depths(faces)
    d_max = max(depth.values())
    max_d_faces = [i for i, d in depth.items() if d == d_max]
    F_idx = max_d_faces[0]
    F = faces[F_idx]
    print(f'\nStep {step}: max depth = {d_max}, F = {F}')
    if d_max == 0:
        print('All depths are 0. DONE.')
        break

    ok, _, fp_idx, e = check_balanced(F_idx, faces, depth)
    if ok:
        Fp = faces[fp_idx]
        print(f'  balanced switch exists on edge {tuple(e)} with F\' = {Fp}.')
        # Apply the balanced switch
        u, v = tuple(e)
        w = [vert for vert in F if vert not in (u, v)][0]
        x = [vert for vert in Fp if vert not in (u, v)][0]
        faces, chords = apply_switch(faces, chords, (u, v), (w, x))
        print(f'  applied balanced switch ({u},{v}) -> ({w},{x})')
        continue

    # Preprocess: pick any depth-(d-1) neighbour and switch
    Fset = set(F)
    chosen = None
    for e_test in [frozenset((F[0], F[1])), frozenset((F[1], F[2])),
                   frozenset((F[0], F[2]))]:
        if e_test in outer_set:
            continue
        cands = [j for j in range(len(faces))
                 if j != F_idx and e_test in face_edges(faces[j])]
        if cands and depth[cands[0]] == d_max - 1:
            chosen = (e_test, cands[0])
            break
    if chosen is None:
        print('  No depth-(d-1) neighbour; cannot preprocess.')
        break
    e, fp_idx = chosen
    Fp = faces[fp_idx]
    u, v = tuple(e)
    w = [vert for vert in F if vert not in (u, v)][0]
    x = [vert for vert in Fp if vert not in (u, v)][0]
    print(f'  preprocessing (unbalanced) switch ({u},{v}) -> ({w},{x})')
    faces, chords = apply_switch(faces, chords, (u, v), (w, x))

print('\nFinal depth distribution:', sorted(compute_depths(faces).values()))
