Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ jobs:
with:
typst-version: ${{ env.TYPST_VERSION }}

# Thai serif faces (Norasi et al.) for bilingual problem files. Without
# this, Thai text still "compiles" but renders as blank tofu boxes.
- name: Install Thai fonts
run: sudo apt-get update && sudo apt-get install -y fonts-thai-tlwg

- name: Check template compiles
run: make template-check

Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ A personal collection of university-level math problems. Each one is written up
as a full solution with the reasoning spelled out and the theorems it leans on
cited, then typeset in [Typst](https://typst.app) and compiled to PDF.

One problem per file. Each file keeps its own metadata (title, source,
difficulty, tags), and the problem index and progress count below are generated
from that metadata, so I don't edit them by hand.
One problem per file. Each file keeps its own metadata (title, source, tags),
and the problem index and progress count below are generated from that metadata,
so I don't edit them by hand. Problems written up in Thai live in a `-th` sibling
file and don't get their own index row.

## Topics

Expand All @@ -35,9 +36,9 @@ their tags.
## Problem index

<!-- INDEX:START (generated by `make index`, don't edit by hand) -->
| # | Problem | Topics | Source | Difficulty | Solved |
|---|---------|--------|--------|------------|--------|
| - | *No problems yet* | | | | |
| # | Problem | Topics | Source | Solved |
|---|---------|--------|--------|--------|
| - | *No problems yet* | | | |

<!-- INDEX:END -->

Expand All @@ -56,6 +57,10 @@ winget install Typst.Typst
winget install jqlang.jq
```

Diagrams use [CeTZ](https://github.com/cetz-package/cetz) (pinned). The first
compile downloads it from the Typst package registry and caches it; CI does the
same automatically.

Then:

```
Expand Down
113 changes: 113 additions & 0 deletions docs/AUTHORING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Authoring

What goes inside a problem file. Naming, branches, and git are in
[CONVENTIONS.md](CONVENTIONS.md).

Start from [`../problems/_TEMPLATE.typ`](../problems/_TEMPLATE.typ).

## Sections

Eight sections, always in this order when present:

```text
Problem Statement → Answer → Setup & Notation → Idea →
Solution → Verification → Reflection → Application
```

Only Problem Statement and Solution are required. Add the others when they
pull their weight — a two-section write-up is fine if the problem is short.

- **Answer** — for evaluate/find problems: box the result with `answer[...]`
up front, then prove it.
- **Idea** — the `#insight` before the formal work.
- **Reflection** — generalizations, alternative proofs, pitfalls. Keep it
within mathematics.
- **Application** — a concrete real-world use (statistics, physics,
computation). Include a cite for each. Skip if there's nothing honest to say.

## Writing the solution

- **Cite every result you use but don't prove.** Any named theorem, rule, or
technique needs a `cite("…", url: "…")` (online) or `cite("…", ref: [Book, §x])`
(offline). This covers claims in any section — Setup, Idea, Verification,
Application — not just `step` reasons. ("no elementary antiderivative" →
cite Liouville; "the tail converges" → cite the comparison test.) A `cite`
without a source prints a red `[ref?]`, so missing references are visible.
Elementary facts (commutativity, basic arithmetic) need no cite. A `#theorem`
you prove in the file is yours — don't cite your own result.

- **Show every step.** Include the algebra between displayed lines. If a reader
has to ask "how did that follow?", the step is missing. Put the rule in
`reason:`.

- **Concrete before abstract.** Before stating the general case, show a small
instance — a specific `n`, a picture, a number.

- **Lead with the idea.** Put an `#insight` naming the key move before the
formal steps.

- **Diagram when the math is visual.** If the problem has geometry or a
visualizable structure, draw it with `diagram(...)` (CeTZ `@preview/cetz:0.3.4`,
pinned) — a shaded region, a lattice, a transformed domain. Make it specific
to the actual problem; theme with `default-theme.accent`. Skip it only when
nothing visual would help.

- **Don't flatten the result.** If the answer is surprising or the method is
non-obvious, say so briefly. Skip this if there's nothing real to add.

- **End with the right environment.** A prove task ends with `#theorem`. An
evaluate/find task ends at the answer — don't re-box a value as a `#theorem`.

- **Verify.** Prove → check an adversarial step. Evaluate → recompute a second
way or numerically. Find-all → show sufficiency *and* "no others". Optimize →
give the witness and the equality case. Count → check small cases by hand.

## Honesty

Don't fabricate a step you can't fill. Report the gap. For an original or
extended problem, if a statement turns out to be unprovable, you mis-stated it
— give the counterexample, then the corrected statement. A wrong proof is worse
than no proof.

## Bilingual (Thai)

Thai siblings share the English file's number with `-th` on the slug
(`calc-001-x.typ` → `calc-001-x-th.typ`; see CONVENTIONS → Bilingual siblings).
Write English first.

- Translate the **prose** — headings, explanations, `reason:` text, insights.
Keep all math, symbols, and proper nouns (Cauchy–Schwarz, Fubini). `cite`
links stay; only the display name is translated.
- Short sentences. One idea each. Don't stack `ซึ่ง…ที่…โดยที่…` clauses.
- **Register: semi-formal (กึ่งทางการ).** Use "เรา" (เราจะแสดงว่า…,
เราจึงได้…) and natural connectives (พิจารณา, สังเกตว่า, จะได้ว่า, ดังนั้น,
นั่นคือ). Not stiff textbook prose.

## Tools (`src/components.typ`, `src/theorems.typ`)

| Tool | For |
|------|-----|
| `theorem` / `lemma` / `corollary` / `proposition` | a numbered result you prove |
| `definition` | a definition |
| `step(reason: …)` | a justified solution step |
| `proof` | a formal proof block (auto QED) |
| `insight` | the key idea callout |
| `answer` | boxed final answer (evaluate/find problems) |
| `cite(name, url:/ref:)` | link a named result you use |
| `diagram(body, caption:)` | a CeTZ figure |
| `numbered(eq)` | number an equation you refer back to |
| `remark` / `example` / `comparison` | asides, tables |

Math is always Typst `$…$` — never plain-text a formula. Equations are
unnumbered by default; wrap only the ones you reference back in `numbered(...)`.
Titles are plain strings — no `$math$`.

## Before committing

- `make compile P=<topic>-NNN` passes, and so does the `-th` sibling if one exists.
- No `[ref?]` markers — every used theorem/rule/technique has a cite.
- No skipped steps.
- Diagram included if the math is visual.
- Problem statement is in your own words if the source had prose; `source` set
(or `""` for an original problem).
- `make index` run.
28 changes: 28 additions & 0 deletions docs/CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ You don't pick the number yourself - `make new T=<topic> S=<slug>` finds the
next free one and copies the template. The pre-commit hook and CI reject any
file in `problems/` that doesn't match the pattern (`_TEMPLATE.typ` is exempt).

### Bilingual (Thai) siblings

A problem can have a Thai-language sibling. It is a separate file (one language
per file, like one problem per file) that **shares the English file's number**
and appends `-th` to the slug:

```text
calc-001-gamma-integral.typ English (primary, write first)
calc-001-gamma-integral-th.typ Thai sibling, same problem, same number
```

The `-th` form still matches the file-name pattern (`-th` is part of the slug),
so no file-name validation change is needed. The Thai file does **not** get a new
number from `make new`; copy the English file and rename it by hand. Thai
rendering relies on the Thai serif faces in the font fallback
(see [`../src/style.typ`](../src/style.typ)); CI installs them via
`fonts-thai-tlwg`.

## Branches

```
Expand Down Expand Up @@ -102,6 +120,16 @@ refs(nt): missing number theory prerequisite links

`template` and repo-wide `refs` changes can drop the `(<topic>-<###>)` part.

## Sourcing results

A hard invariant for every solution: **any result used but not proved in the
file must be traceable.** A named theorem, rule, or technique you invoke without
proving carries a reference the reader can follow — `cite(name, url: ...)` for an
online source or `cite(name, ref: [Book, §x])` for an offline one. Steps that
follow from earlier lines or your own algebra need no citation; elementary
background (commutativity, basic arithmetic) is assumed, not cited. A `cite` with
no source renders a red `[ref?]` marker so the gap is caught before shipping.

## Workflow

```
Expand Down
102 changes: 68 additions & 34 deletions problems/_TEMPLATE.typ
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,31 @@
//
// Before committing:
// - compiles cleanly (make compile P=<topic>-NNN)
// - every theorem/rule used is named, no skipped steps
// - every imported theorem/rule/technique is cited (cite(name, url:) or cite(name, ref:)); no [ref?]
// - no skipped steps — a reader never has to ask "how did that follow?"
// - a diagram is included wherever the math is visual
// - sections in order, only the ones the problem earns (see CONVENTIONS)
// - statement is in your own words if the source had prose
// - source set, or "" for an original problem
// - make index
// Delete these comment lines when done.

#import "../src/template.typ": *
#import "../src/theorems.typ": theorem, lemma, corollary, proposition, definition, proof, insight, step, remark, example
#import "../src/components.typ": comparison, figure-image
#import "../src/components.typ": comparison, figure-image, cite, numbered, answer, diagram
// For a hand-drawn figure, also uncomment these (CeTZ is pinned; the first
// compile downloads it). Draw to the actual math — see #diagram example below.
// #import "../src/style.typ": default-theme
// #import "@preview/cetz:0.3.4"

#show: doc.with(
title: "Problem title here",
title: "Problem title here", // plain text — no $math$ here
subtitle: none, // optional one-line subtitle
source: "e.g. Putnam 2019 B3",
source_url: "https://...", // link to original, or none
source_license: "All rights reserved", // or "CC BY-SA 4.0" (Stack Exchange), "CC BY 4.0"
// author defaults from src/config.typ; override only if needed
date: none, // e.g. "2026-06-16"
difficulty: "medium", // easy | medium | hard
summary: none, // optional one-line abstract under the title
tags: ("calculus", "subtopic-1", "subtopic-2"),
// theme: (accent: rgb("#0a7d55")), // optional per-problem color override
prerequisites: (
Expand All @@ -34,53 +39,82 @@
),
)

// SECTIONS ARE A MENU IN A FIXED ORDER, NOT A CHECKLIST. Only the statement and
// the solution are required. Add the rest *when they earn their place* and keep
// them in the order below — it follows how a reader thinks: what's asked → what's
// the answer → fix notation → the idea → the formal work → check → step back. A
// short evaluation is fine as Statement + Solution alone; don't pad with an empty
// "Reflection". Match the structure to the problem.

// --- REQUIRED ---

= Problem Statement

State the problem precisely. Pure math (equations) may be copied; restate
creative word-problem prose in your own words.

= Setup and Notation

Fix notation, list what is given and what must be shown. Recall any definition
the solution relies on.
// (optional) Answer — for evaluate / find-type problems, show the result up
// front, then prove it below. Skip it when the statement already is the claim.
// #answer[$ integral_0^oo e^(-x) dif x = 1. $]

#definition(title: "Term")[Define any non-standard term used below.]
// --- OPTIONAL, BEFORE the solution (in this order) ---

= Strategy
// = Setup and Notation
// Fix notation and recall definitions — only when the solution carries enough
// machinery that inlining it would clutter the argument.
// #definition(title: "Term")[Define any non-standard term.]

Explain the plan in plain language *before* the formal work: why this approach,
what the obstacle is, and how it is overcome.

#insight[State the single idea that makes the problem tractable.]
// = Idea
// The plan in plain language before the formal work, ending in the key insight —
// only when the approach is non-obvious enough to deserve motivating. For a short
// problem, an #insight at the top of the solution is enough.
// #insight[The one idea that makes the problem tractable.]
//
// A diagram makes the idea visible — include one whenever the math is geometric:
// #diagram(caption: [What the picture shows.])[
// #cetz.canvas(length: 1cm, {
// import cetz.draw: *
// let acc = default-theme.accent
// line((-2.5, 0), (2.5, 0), mark: (end: ">")) // x-axis
// line((0, 0), (0, 1.6), mark: (end: ">")) // y-axis
// // ... draw curves / regions / lattices to the actual problem ...
// })
// ]

= Solution

Break the argument into numbered steps. Justify *every* step with the rule or
theorem it uses, and don't skip steps.

#step(reason: [definition of the object])[
First move. Show the work.
]

#step(reason: [name the theorem / rule applied])[
Next move, with full intermediate algebra:
$ ... = ... $
]
Show every move — including the algebra between two displayed lines — so the
reader never has to reconstruct a jump. Name the rule each step uses in
`reason:`, and `cite` it with a link whenever it is a named theorem or technique
(skip the link only for elementary algebra).

#step(reason: [why this is valid])[
Continue until the result is reached.
#step(reason: [definition / first move])[
Show the work.
]

#proof[
Optional: a formal proof block for the central claim, ending with QED.
#step(reason: cite("Name of the theorem", url: "https://..."))[
Next move, with the intermediate algebra spelled out line by line:
$ a &= b \
&= c \
&= d. $
]

#theorem[State the result that has now been established.]

= Verification
// --- OPTIONAL, AFTER the solution (in this order) ---

Sanity-check the answer: special cases, units, limits, or a numerical check.
// = Verification
// Sanity-check keyed to the task type: small cases, a limit, an equality case,
// or recomputing by another method. Worth it whenever the answer isn't
// self-evidently right.

= Reflection
// = Reflection
// Generalizations, alternative methods, and common pitfalls — within mathematics.
// Only with a genuine point to make. A reference equation can be numbered for
// back-reference: #numbered($ sum_(k=1)^n k = n(n+1)/2 $) <closed>

Can it be generalized? Alternative methods? Common pitfalls? Related results?
// = Application
// Where the result is actually used — other areas of math, science, engineering,
// computation. The real-world hook most solution write-ups skip; it's part of
// what sets this showcase apart. Cite each claimed use, and only include it when
// there's a concrete, honest application — don't stretch for one.
Loading
Loading