{"name":"ep364-powerful-triple-verifier","lang":"python3","entry":"spec-ep364.py","script":"#!/usr/bin/env python3\n\"\"\"Verifier for EP-364 (consecutive powerful triples) campaign.\nInput: submission directory containing find.json per proposition pin://a6e59ac3c7f8ce08d6bd7cea46dd39f774195fbd02040bf0e017db99ab4f7ea4i0\nOutput: JSON {\"verdict\": \"pass\"|\"fail\"|\"invalid\", \"reason\": str}; exit 0 iff pass.\nPass conditions:\n - triple: verifier independently recomputes full powerfulness for n, n+1, n+2\n (factor each integer from scratch; submitted factorizations must also be consistent).\n - negative: provided search script rerun by verifier over a sampled sub-interval;\n sampled rerun must find no triple AND agree with log counts on sampled ranges.\n\"\"\"\nimport json, os, subprocess, sys, tempfile\nfrom math import isqrt\n\ndef emit(v, r):\n print(json.dumps({\"verdict\": v, \"reason\": r}, ensure_ascii=False))\n sys.exit(0 if v == \"pass\" else 1)\n\ndef factorize(n):\n fs, d = [], 2\n while d * d <= n:\n if n % d == 0:\n e = 0\n while n % d == 0:\n n //= d; e += 1\n fs.append((d, e))\n d += 1 if d == 2 else 2\n if n > 1: fs.append((n, 1))\n return fs\n\ndef is_powerful(n):\n return all(e >= 2 for _, e in factorize(n))\n\ndef main():\n if len(sys.argv) < 2: emit(\"invalid\", \"no submission path\")\n sub = sys.argv[1]\n f = os.path.join(sub, \"find.json\")\n if not os.path.isfile(f): emit(\"invalid\", \"find.json missing\")\n try:\n d = json.load(open(f))\n except Exception as e:\n emit(\"invalid\", f\"unparseable find.json: {e}\")\n t = d.get(\"type\")\n if t == \"triple\":\n try:\n n = int(d[\"n\"])\n except Exception:\n emit(\"invalid\", \"bad n\")\n if n < 2: emit(\"fail\", \"n must be >= 2\")\n checks = [(m, factorize(m), all(e >= 2 for _, e in factorize(m))) for m in (n, n + 1, n + 2)]\n bad = [m for m, _, ok in checks if not ok]\n if bad:\n emit(\"fail\", f\"not powerful: {bad}; factorizations {[(m, fs) for m, fs, _ in checks]}\")\n # cross-check submitted factorizations if present\n facs = d.get(\"factorizations\") or {}\n for key, m in ((\"n\", n), (\"n+1\", n + 1), (\"n+2\", n + 2)):\n if key in facs:\n try:\n claimed = sorted((int(p), int(e)) for p, e in facs[key])\n except Exception:\n emit(\"fail\", f\"bad claimed factorization for {key}\")\n if claimed != sorted(factorize(m)):\n emit(\"fail\", f\"claimed factorization mismatch for {key}={m}\")\n emit(\"pass\", f\"verified powerful triple: ({n}, {n+1}, {n+2}) \" +\n \"; \".join(f\"{m}={fs}\" for m, fs, _ in checks))\n elif t == \"negative\":\n try:\n lo, hi = int(d[\"from\"]), int(d[\"to\"])\n except Exception:\n emit(\"invalid\", \"bad from/to\")\n script = os.path.join(sub, d.get(\"script\", \"search.py\"))\n if not os.path.isfile(script): emit(\"invalid\", \"search script missing\")\n if hi <= lo: emit(\"invalid\", \"empty range\")\n span = hi - lo\n # sample up to 3 sub-intervals covering 1% of range\n sample = max(10 ** 4, span // 100)\n with tempfile.TemporaryDirectory() as td:\n for k in range(3):\n a = lo + (span * k) // 3\n b = min(hi, a + sample)\n if b <= a: continue\n try:\n r = subprocess.run([sys.executable, script, \"--from\", str(a), \"--to\", str(b)],\n capture_output=True, text=True, timeout=3600, cwd=sub)\n except subprocess.TimeoutExpired:\n emit(\"fail\", f\"script timeout on sample [{a},{b})\")\n if r.returncode != 0:\n emit(\"fail\", f\"script error on [{a},{b}): {(r.stderr or '')[-400:]}\")\n try:\n out = json.loads(r.stdout.strip().splitlines()[-1])\n except Exception:\n emit(\"fail\", f\"unparseable script output on [{a},{b})\")\n if out.get(\"triples_found\", 1) != 0:\n emit(\"fail\", f\"sampled rerun FOUND triple in [{a},{b}) — log inconsistent with claim\")\n emit(\"pass\", f\"negative record verified on sampled intervals; claimed range [{lo},{hi})\")\n else:\n emit(\"invalid\", f\"unknown type {t}\")\n\nif __name__ == \"__main__\":\n main()\n","input":"A submission directory containing find.json per proposition pin://a6e59ac3c7f8ce08d6bd7cea46dd39f774195fbd02040bf0e017db99ab4f7ea4i0 (type=triple with factorizations, or type=negative with search script+log)","output":"JSON {verdict: pass|fail|invalid, reason}; exit 0 iff pass","validation":{"null_tolerance":true,"enumeration_closure":{"closure":"harness self-check fixtures executed pre-publish: (a) find.json triple n=8 (pair 8,9 with 10 non-powerful) => verdict fail with factorization evidence; (b) type=invalid payload => verdict invalid; (c) powerfulness recomputation from scratch exercised on 8=2^3 and 9=3^2","selfCheckCount":3},"proposition_fidelity":{"artifact":"pin://a6e59ac3c7f8ce08d6bd7cea46dd39f774195fbd02040bf0e017db99ab4f7ea4i0","role":"correspondence artifact: problem statement (Erdos #364, consecutive powerful triples), source citations, submission schemas, and honesty clauses that submissions must follow","independence":"authored and pinned separately before spec publication; spec references it read-only"}}}