Files
TOIFAS/code/make_tables.py
T
KiHoLee 0645347685 Carry the figure-style and table-order fixes into the release
The lower of two coinciding curves is the wide one, the random-guess
reference is dotted everywhere and never borrows a scheme colour, the
three collection SNRs in the known-plaintext figure differ by face and
width rather than by dash alone, and the legend floor now prints at six
points with an assertion that fails if any figure drags it lower. Both
table generators list the proposal first.
2026-08-29 00:15:02 +09:00

112 lines
4.1 KiB
Python

"""Generate the LaTeX rows of every result table from the CSVs, so
that every table in the paper is reproducible from data/ (TIFS mandate).
Prints the tabular bodies; paste into main.tex without edits.
"""
from __future__ import annotations
import csv
import json
import math
from pathlib import Path
DATA = Path(__file__).resolve().parents[1] / "data"
NAME = {
"proposed": r"\textbf{KM (str.)}",
"proposed_learned": r"\textbf{KM (lrn.)}",
"public_mask": "Public masks",
"perm_key": r"Permutation key~\cite{chen2025shufflingtifs}",
"index_cipher": "Index cipher",
"oma_plain": "OMA (no encryption)",
"random": "Random",
"hadamard": "Structured",
"learned": "Learned, plain",
"learned_reg": r"Learned, regularized~\eqref{eq:regloss}",
"invariant_learned": r"\textbf{Invariant, KM (lrn.)}",
}
RECEIVER = {
"legit": "Legitimate", "oma": "OMA",
"insider": "Insider", "eve": "Outsider",
}
def f3(x: str) -> str:
"""Three decimals, or an em-dash for a value that does not apply."""
try:
v = float(x)
except (TypeError, ValueError):
return "--"
return "--" if math.isnan(v) else f"{v:.3f}"
def f4(x: str) -> str:
"""Four decimals, for a column whose values sit against the
random-guess level and would otherwise all print as 1.000 while the
body quotes their distance from it in units of 1e-4."""
try:
v = float(x)
except (TypeError, ValueError):
return "--"
return "--" if math.isnan(v) else f"{v:.4f}"
def cell(x: str, bold: bool, wide: bool = False) -> str:
s = f4(x) if wide else f3(x)
if s == "--":
return "--"
return rf"$\mathbf{{{s}}}$" if bold else f"${s}$"
def compare_table():
print("% Table: scheme comparison (from sec_compare.csv)")
rows = list(csv.DictReader(open(DATA / "sec_compare.csv")))
# 9.3: the proposal first, as every figure legend lists it
order = ["proposed", "proposed_learned", "public_mask", "perm_key",
"index_cipher", "oma_plain"]
rows.sort(key=lambda r: order.index(r["scheme"]))
# stage_E does not jam the orthogonal reference, because the jammer an
# OMA user faces is targeted at public slots rather than mask-matched
# or mask-blind. stage_L measures that case, so the cell comes from
# there instead of being left empty.
jam = {float(r["jsr_db"]): r
for r in csv.DictReader(open(DATA / "sec_jam_cmp.csv"))}
oma_jam = jam[0.0]["oma_targeted"]
for r in rows:
b = r["scheme"].startswith("proposed")
if r["scheme"] == "oma_plain" and f3(r["jam0_ser"]) == "--":
r["jam0_ser"] = oma_jam
# four decimals would still print 1.0000 here, so the column
# stays at three and the caption names the chance level
cells = [cell(r[k], b) for k in
("eve_out", "eve_in", "jam0_ser")]
print(f"{NAME[r['scheme']]} & " + " & ".join(cells) + r" \\")
def maskfam_table():
print("% Table: key families (from sec_maskfam.csv)")
for r in csv.DictReader(open(DATA / "sec_maskfam.csv")):
# the structured family is the main configuration, so its row is
# emphasized the same way the proposed row is in the comparison
b = r["family"] == "hadamard"
cells = [cell(r[k], b) for k in
("legit_ser", "eve_ser", "mask_xcorr")]
name = NAME[r["family"]]
if b:
name = r"\textbf{" + name + "}"
print(f"{name} & " + " & ".join(cells) + r" \\")
def refresh_tables():
print("% Table: key refresh (from refresh_summary.csv)")
for r in csv.DictReader(open(DATA / "refresh_summary.csv")):
b = r["scheme"].startswith("Invariant")
name = (r"\textbf{" + r["scheme"] + "}") if b else r["scheme"]
f = (lambda t: r"\mathbf{" + t + "}") if b else (lambda t: t)
print(f"{name} & ${f(format(float(r['legit']), '.3f'))}$ & "
f"${f(format(float(r['eve']), '.4f'))}$ & "
# the unit lives in the header, not in every cell
f"${f(format(float(r['entropy_bits']), '.1f'))}$" + r" \\")
if __name__ == "__main__":
compare_table(); print()
maskfam_table(); print()
refresh_tables()