# -*- coding: utf-8 -*- """ collatz_verify_coverage.py Load certificate, check union of residue classes, output markdown with coverage analysis. Usage: python collatz_verify_coverage.py --certificat JSON_PATH --output MD_PATH """ from __future__ import annotations import argparse import json from pathlib import Path from collections import defaultdict def covered_residues(clauses: list) -> dict[int, set[int]]: """ Build mod -> set of residues from clauses. Supports: dict with 'mod'/'residue', or int (residue) with inferred modulus. """ mod_to_residues: dict[int, set[int]] = defaultdict(set) for c in clauses: if isinstance(c, (int, float)): r = int(c) m = 0 while (1 << m) <= r: m += 1 mod_val = 1 << max(m, 1) mod_to_residues[mod_val].add(r) elif isinstance(c, dict): mod_val = c.get("mod") residue_val = c.get("residue") if mod_val is not None and residue_val is not None: mod_to_residues[int(mod_val)].add(int(residue_val)) return dict(mod_to_residues) def analyze_coverage(mod_to_residues: dict[int, set[int]]) -> list[str]: """Produce markdown lines for coverage analysis.""" lines: list[str] = [] lines.append("# Coverage Analysis") lines.append("") lines.append("## Union of residue classes") lines.append("") total_clauses = sum(len(r) for r in mod_to_residues.values()) lines.append(f"- Total clauses: {total_clauses}") lines.append("") for mod_val in sorted(mod_to_residues.keys()): residues = mod_to_residues[mod_val] # Odd residues mod 2^m: 2^(m-1) classes odd_count = mod_val // 2 coverage_pct = (len(residues) / odd_count * 100) if odd_count else 0 lines.append(f"### Modulus 2^{mod_val.bit_length() - 1} (mod = {mod_val})") lines.append("") lines.append(f"- Residue classes covered: {len(residues)} / {odd_count}") lines.append(f"- Coverage: {coverage_pct:.2f}%") lines.append("") return lines def main() -> None: parser = argparse.ArgumentParser( description="Verify coverage of certificate (union of residue classes)" ) parser.add_argument("--certificat", required=True, help="Path to certificate JSON") parser.add_argument("--output", required=True, help="Output markdown path") args = parser.parse_args() cert_path = Path(args.certificat) if not cert_path.is_file(): raise SystemExit(f"Not a file: {cert_path}") data = json.loads(cert_path.read_text(encoding="utf-8")) clauses = data.get("clauses", data.get("closed", [])) mod_to_residues = covered_residues(clauses) lines = analyze_coverage(mod_to_residues) out_path = Path(args.output) out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text("\n".join(lines) + "\n", encoding="utf-8") if __name__ == "__main__": main()