"""Step 2: For pairs of adjacent tires (T1, T2) sharing a cycle γ,
compute the intersection of T1's D-projection and T2's U-projection
on γ.  Compatibility (chain-pigeonhole step) succeeds iff the
intersection is non-empty.

Conventions
-----------
* T1 is the OUTER tire: γ = B_in^(1). T1's inner-spoke pattern on γ
  is its D-projection π_D^(1).
* T2 is the INNER tire: γ = B_out^(2). T2's outer-spoke pattern on γ
  is its U-projection π_U^(2).
* Each tire's σ-support is closed under cyclic rotation of its dual
  cycle, which induces cyclic rotation on the γ-projection.
* T1 and T2 may walk around γ in opposite orientations, so we check
  intersection both forward and with one side reversed.

Two models per tire:
  SR -- Steiner-rich (no chord effect; spoke-only baseline from step 1)
  SP -- Steiner-poor (each O-face is one G-face; chord constraints active)
"""
from itertools import product

from tire_fiber_chords import (
    COLORS,
    fiber_distribution,
    spoke_only_fiber_distribution,
    d_positions_for,
    u_positions_for,
    projection_support,
)


def project_T1_D(gamma_len: int, m_1: int, chords_1, model: str) -> set:
    """T1's D-projection on γ.  T1 has B_out of length m_1, B_in = γ
    of length gamma_len."""
    if model == 'SR':
        n = m_1 + gamma_len
        fibers = spoke_only_fiber_distribution(n)
    else:
        fibers, _, _ = fiber_distribution(m_1, gamma_len, chords_1)
    d_pos = d_positions_for(m_1, gamma_len)
    return projection_support(fibers, d_pos)


def project_T2_U(gamma_len: int, k_2: int, chords_2, model: str) -> set:
    """T2's U-projection on γ.  T2 has B_out = γ of length gamma_len,
    B_in of length k_2."""
    if model == 'SR':
        n = gamma_len + k_2
        fibers = spoke_only_fiber_distribution(n)
    else:
        fibers, _, _ = fiber_distribution(gamma_len, k_2, chords_2)
    u_pos = u_positions_for(gamma_len, k_2)
    return projection_support(fibers, u_pos)


def intersect_with_reflection(S1: set, S2: set) -> tuple[set, set]:
    """Return (forward intersection, reflection-flipped intersection)."""
    forward = S1 & S2
    S2_rev = {s[::-1] for s in S2}
    reverse = S1 & S2_rev
    return forward, reverse


CASES = [
    # (γ, T1=(m_1, chords_1, model1), T2=(k_2, chords_2, model2))
    # γ = 3: no chord constraints possible (C_3 has no chords)
    (3, (3, [], 'SR'), (3, [], 'SR')),
    (3, (3, [], 'SR'), (4, [(0,2)], 'SP')),
    (3, (3, [], 'SP'), (3, [], 'SP')),
    (3, (3, [], 'SP'), (4, [(0,2)], 'SP')),

    # γ = 4: SP-feasible only with chord (0,2) on T1
    (4, (4, [], 'SR'), (4, [], 'SR')),
    (4, (4, [(0,2)], 'SP'), (4, [(0,2)], 'SP')),
    (4, (4, [(0,2)], 'SP'), (4, [], 'SR')),
    (4, (4, [],    'SR'), (4, [(0,2)], 'SP')),
    (4, (3, [(0,2)], 'SP'), (4, [(0,2)], 'SP')),
    (4, (4, [(0,2)], 'SP'), (5, [(0,2)], 'SP')),
    (4, (4, [(0,2)], 'SP'), (6, [(0,3)], 'SP')),
    (4, (4, [(0,2)], 'SP'), (6, [(0,2),(3,5)], 'SP')),

    # γ = 5
    (5, (5, [(0,2)], 'SP'), (3, [],     'SR')),
    (5, (5, [(0,2)], 'SP'), (5, [(0,2)], 'SP')),
    (5, (5, [(0,2)], 'SP'), (5, [(0,3)], 'SP')),
    (5, (5, [(0,3)], 'SP'), (5, [(0,3)], 'SP')),
    (5, (5, [(0,2)], 'SR'), (5, [(0,2)], 'SP')),

    # γ = 6
    (6, (6, [(0,3)], 'SP'), (6, [(0,3)], 'SP')),
    (6, (6, [(0,3)], 'SP'), (6, [(0,2),(3,5)], 'SP')),
    (6, (6, [(0,2),(3,5)], 'SP'), (6, [(0,2),(3,5)], 'SP')),
    (6, (6, [(0,3)], 'SP'), (3, [], 'SR')),
    (6, (6, [(0,2),(3,5)], 'SP'), (3, [], 'SR')),
    (6, (6, [(0,2),(3,5)], 'SP'), (4, [(0,2)], 'SP')),
]


def fmt_cfg(m_or_k: int, chords, model: str) -> str:
    ch_str = str(chords) if chords else "—"
    return f"({m_or_k}, {ch_str}, {model})"


def main():
    print(f"{'γ':>2}  {'T1 (m_1, chords_1, model)':<32s}  {'T2 (k_2, chords_2, model)':<32s}  "
          f"{'|S1|':>5s}  {'|S2|':>5s}  {'3^γ':>5s}  {'fwd':>5s}  {'rev':>5s}  {'compat?':>7s}")
    print("-" * 120)
    nyes = ntotal = 0
    for gamma, t1, t2 in CASES:
        m_1, ch1, model1 = t1
        k_2, ch2, model2 = t2
        S1 = project_T1_D(gamma, m_1, ch1, model1)
        S2 = project_T2_U(gamma, k_2, ch2, model2)
        forward, reverse = intersect_with_reflection(S1, S2)
        compat = "YES" if (forward or reverse) else "NO"
        ntotal += 1
        if compat == "YES":
            nyes += 1
        print(
            f"{gamma:>2}  {fmt_cfg(m_1, ch1, model1):<32s}  {fmt_cfg(k_2, ch2, model2):<32s}  "
            f"{len(S1):>5d}  {len(S2):>5d}  {3**gamma:>5d}  "
            f"{len(forward):>5d}  {len(reverse):>5d}  {compat:>7s}"
        )
    print("-" * 120)
    print(f"{nyes}/{ntotal} compatible.")


if __name__ == '__main__':
    main()
