Appearance
qgen — the DSA Question Pipeline
tools/qgen/qgen.py turns a drafted DSA question into a production-ready, fuzz-tested, cross-language-verified markdown file in the website's src/content/. It exists because the failure mode it prevents is embarrassing and real: a reference solution that doesn't pass its own tests — which happened, once, and never again after verify_solution.py became a mandatory gate.
The 10-step model
Steps 1–3 and 9–10 are human; qgen automates 4–8:
| # | Step | Who |
|---|---|---|
| 1–3 | decide the question, AI-draft the structured markdown, save to drafts/<slug>.md | you |
| 4 | build_tests.sh — compile solutions, run the generator, produce cases | qgen |
| 5 | fix_case_spread.py / trim_oversized_cases.py — balance difficulty spread, trim size | qgen |
| 6 | verify_solution.py — cross-verify every language's solution against every case | qgen |
| 7 | lint-content.mjs (the website's copy, via site_config.py) | qgen |
| 8 | move into ediky-website/ediky/src/content/<section>/, audit_sizes.py | qgen |
| 9–10 | smoke-test in the running site, commit | you |
Presets: sized to the problem, not one-size
presets.json defines three tiers — matched automatically from the draft's frontmatter topic keywords, overridable by flag:
simple | standard | complex | |
|---|---|---|---|
| generated cases | 60 | 100 | 120 |
| solution timeout | 5 s | 10 s | 12 s |
| fuzz iterations | 1,500 | 5,000 | 10,000 |
| max stdin / expected | 250 / 100 KB | 200 / 100 KB | 400 / 200 KB |
| post-trim budget | 150 KB / 40 cases | 250 KB / 45 | higher |
simple auto-matches topics like string, array, sorting, two pointers, hashing, implementation; unmatched topics get standard. The trim budgets exist because of the sidecar economics in Question Bank — cases users download on Submit should be as small as correctness allows.
What "verified" means here
- Independent brute-force verifier: the draft includes a second, simpler solution;
fuzz.pythrows thousands of random inputs at both and diffs outputs. A disagreement fails the pipeline — this catches wrong reference solutions, not just wrong tests. - Cross-language: C++17, Python, and Java reference solutions must all pass all cases (
langs.pyhandles per-language compile/run). If Python TLEs where C++ passes, the case sizes (or the per-language multipliers on the website side) are wrong — better to find out here than in production. - Adversarial cases: generators are written to include edge patterns (empties, max-N, degenerate structures), not just random noise.
Cross-platform detail that saves an afternoon
The verifiers auto-detect the Python interpreter on Windows (py -3 → python → python3), so the same qgen.py invocation works on the Windows authoring machine and any Unix box without editing scripts.
The screenshot-to-question workflow
A large share of the bank came from real OA screenshots (D.E. Shaw, Flipkart, Okta, Cisco, HackerRank): screenshot → AI-assisted transcription into the structured draft format (statement, constraints, solutions in three languages, generator, brute verifier) → qgen. The pipeline is what makes that workflow trustworthy — a transcription error in constraints or expected behavior gets caught by the brute-force diff instead of shipping.
Failure exit codes
qgen fails loudly with the stage that broke (generation, spread-fix, verification, lint, audit). The most common real failures: a generator emitting out-of-constraint inputs (caught by verify), and oversized cases (caught by audit) — both fixed in the draft, never by loosening the gate.