oxedyne/fe2o3/fe2o3_austenite/tools/bench/gen_synthetic.py
3.1 KiB, 1 run, executable
created by r1870400018:60941, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | #!/usr/bin/env python3 |
| 2 | """Generates a deterministic synthetic Typst document for the S0 bench harness. |
| 3 | |
| 4 | No book content: every word is drawn from a small fixed vocabulary by a seeded |
| 5 | PRNG, so the text carries no meaning and no licence. Each page mixes a heading, |
| 6 | two or three prose paragraphs, and -- on every fifth page -- a small table or a |
| 7 | maths block, so the document exercises more than the pure-text path without |
| 8 | pulling in cetz or other package fixtures S0 was not asked to touch. |
| 9 | |
| 10 | Every paragraph opens with a sentinel token `EDITTOK{n}` (n = 1-based paragraph |
| 11 | index across the whole document), so `edit_latency.mjs` can find paragraph N by |
| 12 | string search and flip one character in it without parsing the document. |
| 13 | |
| 14 | Usage: gen_synthetic.py <pages> <out.typ> |
| 15 | """ |
| 16 | import random |
| 17 | import sys |
| 18 | |
| 19 | VOCAB = ( |
| 20 | "lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod " |
| 21 | "tempor incididunt ut labore et dolore magna aliqua enim ad minim veniam " |
| 22 | "quis nostrud exercitation ullamco laboris nisi aliquip ex ea commodo " |
| 23 | "consequat duis aute irure in reprehenderit voluptate velit esse cillum " |
| 24 | "eu fugiat nulla pariatur excepteur sint occaecat cupidatat non proident" |
| 25 | ).split() |
| 26 | |
| 27 | |
| 28 | def sentence(rng, n_words): |
| 29 | words = [rng.choice(VOCAB) for _ in range(n_words)] |
| 30 | words[0] = words[0].capitalize() |
| 31 | return " ".join(words) + "." |
| 32 | |
| 33 | |
| 34 | def paragraph(rng, tok): |
| 35 | sentences = [sentence(rng, rng.randint(6, 14)) for _ in range(rng.randint(3, 6))] |
| 36 | return f"{tok} " + " ".join(sentences) |
| 37 | |
| 38 | |
| 39 | def table_block(rng, page): |
| 40 | rows = [] |
| 41 | for r in range(4): |
| 42 | cells = [str(rng.randint(0, 9999)) for _ in range(3)] |
| 43 | rows.append(" [" + "], [".join(cells) + "],") |
| 44 | body = "\n".join(rows) |
| 45 | return ( |
| 46 | f"#table(\n columns: 3,\n [A], [B], [C],\n{body}\n)" |
| 47 | ) |
| 48 | |
| 49 | |
| 50 | def maths_block(rng, page): |
| 51 | a, b, c = rng.randint(2, 9), rng.randint(2, 9), rng.randint(2, 9) |
| 52 | return f"$ ({a}x + {b})^{c} = sum_(k=0)^{c} binom({c}, k) ({a}x)^k {b}^({c}-k) $" |
| 53 | |
| 54 | |
| 55 | def main(): |
| 56 | if len(sys.argv) != 3: |
| 57 | print("usage: gen_synthetic.py <pages> <out.typ>", file=sys.stderr) |
| 58 | sys.exit(2) |
| 59 | pages = int(sys.argv[1]) |
| 60 | out_path = sys.argv[2] |
| 61 | rng = random.Random(20260923) # fixed seed: reproducible across runs and machines |
| 62 | |
| 63 | lines = [ |
| 64 | "// Generated by tools/bench/gen_synthetic.py -- synthetic, no book content.", |
| 65 | "// Regenerate rather than hand-edit; edit_latency.mjs depends on the EDITTOK", |
| 66 | "// markers below staying at paragraph-start.", |
| 67 | "#set page(width: 15cm, height: 21cm, margin: 2cm)", |
| 68 | "#set text(size: 10pt)", |
| 69 | "#set par(justify: true)", |
| 70 | "", |
| 71 | ] |
| 72 | tok = 0 |
| 73 | for page in range(1, pages + 1): |
| 74 | lines.append(f"= Section {page}") |
| 75 | n_paras = 2 if page % 5 == 0 else 3 |
| 76 | for _ in range(n_paras): |
| 77 | tok += 1 |
| 78 | lines.append(paragraph(rng, f"EDITTOK{tok}")) |
| 79 | lines.append("") |
| 80 | if page % 10 == 0: |
| 81 | lines.append(maths_block(rng, page)) |
| 82 | lines.append("") |
| 83 | elif page % 5 == 0: |
| 84 | lines.append(table_block(rng, page)) |
| 85 | lines.append("") |
| 86 | if page != pages: |
| 87 | lines.append("#pagebreak()") |
| 88 | lines.append("") |
| 89 | |
| 90 | with open(out_path, "w") as f: |
| 91 | f.write("\n".join(lines) + "\n") |
| 92 | print(f"wrote {out_path}: {pages} pages, {tok} paragraphs", file=sys.stderr) |
| 93 | |
| 94 | |
| 95 | if __name__ == "__main__": |
| 96 | main() |