"""Shared helpers: verification of a schedule, and counting facts.""" from itertools import combinations def all_pairs(n=24): return set(combinations(range(n), 2)) def pairs_covered_by_day(day): """day: list of 4 boats, each a list of 6 ints. Return set of covered pairs.""" cov = set() for boat in day: for a, b in combinations(sorted(boat), 2): cov.add((a, b)) return cov def verify(schedule, n=24, boats=4, size=6): """Validate structure and full pair coverage. Returns (ok, info).""" info = {} need = all_pairs(n) covered = set() for d, day in enumerate(schedule): assert len(day) == boats, f"day {d}: not {boats} boats" flat = [p for boat in day for p in boat] assert sorted(flat) == list(range(n)), f"day {d}: not a partition of 0..{n-1}" for boat in day: assert len(boat) == size, f"day {d}: boat size != {size}" covered |= pairs_covered_by_day(day) missing = need - covered info['days'] = len(schedule) info['covered'] = len(covered) info['missing'] = len(missing) info['missing_pairs'] = sorted(missing) # meeting multiplicities mult = {} for day in schedule: for boat in day: for a, b in combinations(sorted(boat), 2): mult[(a, b)] = mult.get((a, b), 0) + 1 info['total_meetings'] = sum(mult.values()) info['repeat_excess'] = sum(m - 1 for m in mult.values()) return (len(missing) == 0), info if __name__ == "__main__": # Sanity: counting facts print("C(24,2) =", len(all_pairs())) print("pairs covered per boat C(6,2) =", 15) print("pairs covered per day = 4*15 =", 60) for d in range(4, 8): print(f"d={d}: 60*d = {60*d} meetings, excess if full cover = {60*d - 276}, " f"per-person meetings 5*{d}={5*d} (need 23)")