From 47d974a5bffc99bd50a45be56a064844ef324a5a Mon Sep 17 00:00:00 2001 From: KiHoLee Date: Mon, 17 Aug 2026 00:06:25 +0900 Subject: [PATCH] Add check_consistency.py, correct two stale figures in comments check_consistency.py asserts every headline number against its raw CSV, so a stale quoted value fails loudly instead of surviving a revision. It skips the manuscript-side assertions when main.tex is absent, which is the case in this package. The blind-versus-permutation agreement is 0.002, not 0.0015, and the resource-matched OMA is undefined below L=16 rather than wherever 16/L is not an integer. --- code/check_consistency.py | 93 +++++++++++++++++++++++++++++++++++++++ code/replot_security.py | 4 +- 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 code/check_consistency.py diff --git a/code/check_consistency.py b/code/check_consistency.py new file mode 100644 index 0000000..d03417b --- /dev/null +++ b/code/check_consistency.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +"""Final consistency check: every headline number vs its raw CSV.""" +import csv +import math +import re +from pathlib import Path + +base = Path(__file__).resolve().parents[1] +# The manuscript is not part of the reproducibility package, so the +# tex-side assertions are skipped when it is absent and the data-side +# assertions still run. +_tex_path = base / "main.tex" +HAVE_TEX = _tex_path.exists() +tex = _tex_path.read_text(encoding="utf-8") if HAVE_TEX else "" + + +def rows(name): + with open(base / "data" / name) as f: + return list(csv.DictReader(f)) + + +def col(name, k): + return [float(r[k]) for r in rows(name) if r[k] != "nan"] + + +ok = True + + +def chk(label, cond, detail, needs_tex=False): + global ok + if needs_tex and not HAVE_TEX: + print(" SKIP " + label + " :: main.tex not in this package") + return + print((" PASS " if cond else " FAIL ") + label + " :: " + detail) + if not cond: + ok = False + + +print("headline numbers vs raw data") + +# 1.27x key-length ratio +k = rows("sec_keylen.csv") +r64 = [x for x in k if int(x["L"]) == 64][0] +ratio = float(r64["oma"]) / float(r64["legit_ser"]) +chk("key-length ratio 1.27", round(ratio, 2) == 1.27, "%.4f" % ratio) +chk("1.27 in tex", tex.count("1.27") >= 2, "%d occurrences" % tex.count("1.27"), needs_tex=True) + +# blind-jammer gap +g = col("sec_jam_gap.csv", "gap_db") +chk("gap 7.4-8.1 dB", round(min(g), 1) == 7.4 and round(max(g), 1) == 8.1, + "%.3f to %.3f" % (min(g), max(g))) +chk("no stale 8.5 dB", "8.5$~dB" not in tex, "searched tex", needs_tex=True) +lin = (10 ** (min(g) / 10), 10 ** (max(g) / 10)) +chk("about six times power", lin[0] < 6.5 and lin[1] > 5.5, + "%.2f to %.2f" % lin) + +# blind vs permutation +j = rows("sec_jam_cmp.csv") +dmax = max(abs(float(r["blind"]) - float(r["perm_blind"])) for r in j) +chk("within 0.002", dmax <= 0.002, "%.5f" % dmax) +chk("no stale 0.0015 in jamming", "$0.0015$ of the proposed" not in tex, "ok", needs_tex=True) + +# brute force +b = rows("sec_brute_cmp.csv") +sm = float(b[-1]["ser_mask"]) +chk("brute 0.76 both places", tex.count("$0.76$") >= 2, "%.4f measured" % sm, needs_tex=True) +chk("no stale 0.75 in summary", + "$0.75$ after $10^{6}$" not in tex, "summary row", needs_tex=True) + +# refresh +rs = {r["scheme"]: r for r in rows("refresh_summary.csv")} +chk("refresh 64.8 bits", + round(float(rs["Invariant"]["entropy_bits"]), 1) == 64.8, + "%.3f" % float(rs["Invariant"]["entropy_bits"])) +chk("fixed key 15.0 bits", + round(float(rs["None (fixed key)"]["entropy_bits"]), 1) == 15.0, + "%.4f" % float(rs["None (fixed key)"]["entropy_bits"])) + +# permutation KPA +pk = rows("pkpa.csv") +p6 = float([r for r in pk if r["n_frames"] == "6"][0]["eve_ser"]) +chk("perm KPA at N=6 near 0.303", abs(p6 - 0.303) < 0.005, "%.4f" % p6) + +# abstract +a = (tex.split(r"\begin{abstract}")[1].split(r"\end{abstract}")[0].strip() + if HAVE_TEX else "") +w = len(re.split(r"\s+", a)) if a else 0 +chk("abstract <= 250 words", w <= 250, "%d words" % w, needs_tex=True) +chk("abstract has no abbreviations", + not re.findall(r"\b[A-Z]{2,}\b", a), str(re.findall(r"\b[A-Z]{2,}\b", a)), needs_tex=True) + +print() +print("ALL CONSISTENT" if ok else "INCONSISTENCIES FOUND") diff --git a/code/replot_security.py b/code/replot_security.py index fed2e8b..edced97 100644 --- a/code/replot_security.py +++ b/code/replot_security.py @@ -136,7 +136,7 @@ def fig_snr(): def fig_keylen(): """The OMA reference is the resource-matched one of oma_ser_keylen, - which is undefined at key lengths where 16/L is not an integer; those + which is undefined below L=16 unless 16/L is an integer; those rows carry nan and are skipped.""" r = load("sec_keylen.csv") x = col(r, "L", int) @@ -169,7 +169,7 @@ def fig_jam(): markevery=me, label=LBL["mask"] + ", matched") ax.plot(x, col(r, "oma_targeted"), color=C_PUB, marker="^", ls=":", markevery=me, label=LBL["oma"] + ", targeted") - # the two blind curves agree to 0.0015; deliberate layering + # the two blind curves agree to 0.002; deliberate layering ax.plot(x, col(r, "blind"), color=C_LEGIT, marker="o", ls="-", markevery=me, label=LBL["mask"] + ", blind", **UNDER) ax.plot(x, col(r, "perm_blind"), color=C_EVE, marker="s", ls="-.",