"""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()))