from __future__ import annotations
import json
import pathlib
import sys
from collections import Counter
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from decode import BlockedDafsa
def rot_min(t: tuple) -> tuple:
return min(t[i:] + t[:i] for i in range(len(t)))
def recompute(bd: BlockedDafsa):
free = Counter(); subring = Counter(); coset = Counter()
achiral = Counter(); rotsym = Counter(); symm = Counter()
for rat in bd.iter_rats():
t = tuple(rat); L = len(t)
free[L] += 1
if all(a % 2 == 0 for a in t):
subring[L] += 1
if all(a % 2 != 0 for a in t):
coset[L] += 1
ach = rot_min(t) == rot_min(t[::-1])
rot = any(t[d:] + t[:d] == t for d in range(1, L))
if ach:
achiral[L] += 1
if rot:
rotsym[L] += 1
if ach or rot:
symm[L] += 1
one_sided = {n: 2 * free[n] - achiral[n] for n in free}
return {
"free": free, "oneSided": one_sided, "achiral": achiral,
"rotationSymmetric": rotsym, "symmetric": symm,
"subring": subring, "coset": coset,
}
def series(counts, max_perim: int) -> str:
return ",".join(str(counts.get(n, 0)) for n in range(1, max_perim + 1))
def main(argv: list[str]) -> int:
d = pathlib.Path(argv[1] if len(argv) >= 2 else ".")
if not (d / "block_index.json").exists() or not (d / "ro-crate-metadata.json").exists():
sys.stderr.write(f"need block_index.json + ro-crate-metadata.json in {d}\n")
return 2
max_perim = json.loads((d / "block_index.json").read_text())["max_indexed_length"]
crate = json.loads((d / "ro-crate-metadata.json").read_text())
emitted: dict[str, str] = {}
for e in crate.get("@graph", []):
if e.get("@id") == "./" or e.get("@type") == "Dataset":
for pv in (e.get("variableMeasured") or []):
emitted[pv["name"]] = pv["value"]
if not emitted:
sys.stderr.write("no variableMeasured in ro-crate-metadata.json\n")
return 2
recomputed = recompute(BlockedDafsa(d))
bad = 0
for name, counts in recomputed.items():
want = series(counts, max_perim)
got = emitted.get(name)
if got != want:
bad += 1
print(f"{name}: MISMATCH\n emitted: {got}\n recomputed: {want}")
if bad:
print(f"{bad} sequence(s) disagree")
return 1
print(f"OK ({len(recomputed)} sequences verified to perimeter {max_perim})")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))