Skip to content
kjulluPublic

About

Made by codex (GPT-5.6 Sol) to do physics calculations in typst.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

math-once

Write a calculation once, show the equation, and reuse its exact result. math-once is a Typst calculator with physical units, automatic conversion, stateful equation variables, and symbolic calculations through typCAS.

Use calculation-builder(digits: auto, auto-digits: "significant") to make automatic rounding follow significant figures instead of decimal places. See rounding options.

Unknown quoted unit names also work as opaque custom units. They support ordinary arithmetic with matching custom units, without physical conversion.

#import "math-once.typ": calculation-builder, text-unit

#let eq = calculation-builder(digits: 2)

#eq($ v := 902 / 3.6 $)
// v = 902/3.6 = 250.56

#eq($ v * 2 $)
// v ⋅ 2 = 250.56 ⋅ 2 = 501.11

#eq($ 2 pi $)
// 2π = 2 ⋅ 3.141592653589793 = 6.28

#eq($ floor(3.7) $)
// floor(3.7) = 3

#eq($ x := |1 - 4| $)
// x = |1 - 4| = 3

#eq($ 10 plus.minus 2 $)
// 10 ± 2 = 12 ∨ 8

#eq($ arrow(v) := vec(1, 2) $)
#eq($ X := mat(1, 2; 3, 4) $)
#eq($ X arrow(v) $)
// Xv⃗ = vec(5, 11)

Conclusion: #eq($v$, result-only: true)
// Conclusion: 250.56

It takes #eq($1.394 s$, result-only: true, show-unit: false) seconds.
// show-unit avoids output such as "1.39 s seconds"

#eq($ "distance" := 0.5 "mm" $)
#eq($ 1 / "distance" $, unit: $#text-unit("lines") / m $)
// 1/distance = 1/(0.5 mm) = 2000 lines/m

// Use raw backticks for CAS input. Plain $...$ only works when
// multi-letter names are quoted.
#eq(` f := simplify(x^2 + 2*x + 1) `)
#eq(` df := diff(f, x) `)
// df = diff(f, x) = 2x + 2

// Raw input has no $...$ layout metadata. Override its centered default when
// a normal CAS result should appear inline:
Inline derivative: #eq(`diff(f, x)`, block: false)

// Alternatively, quote multi-letter variables and CAS operations in $...$:
#eq($ "identity" := "simplify"(sin(x)^2 + cos(x)^2) $)

Acknowledgements and alternatives

  • Qalculate! is a powerful calculator. Its unit handling inspired math-once. (Terminal and GUI tool)
  • eqrun is a well-designed calculator for Typst and the original inspiration for this project. (Typst plugin)
  • typCAS provides the symbolic algebra used by math-once. (Typst package)

Install

Copy math-once.typ into your project and import its functions:

#import "math-once.typ": *

When installed as a local Typst package, use:

#import "@local/math-once:0.45.0": *

* imports all exported names. You can instead list only the functions you need, for example #import "math-once.typ": calculation-builder.

The package is implemented entirely in Typst. Symbolic builder operations use the pinned @preview/typcas:0.2.3 dependency, which Typst downloads and caches through the normal package system. See third-party notices.

Common calculation mistakes passed through calculation-builder are shown as centered red messages in the document instead of stopping compilation. Direct calculate(...) calls still panic, making failures observable in Typst code. Set strict: true on a calculation builder when its calculation errors should stop compilation, such as in CI. Set strict-units: true on calculate or a builder to reject unknown quoted units instead of treating them as opaque custom units.

Git release archives contain examples/all-functions.typ, CHANGELOG.md, the complete test suite, and the unit-audit tools linked from this README. A registry submission should use the complete repository tree so those relative links remain valid.

Documentation

Start with calculate for one unit-aware expression, or calculation-builder to store variables and show substitutions across equations.

A compilable example covering the complete public API is available in examples/all-functions.typ.

You can find all the supported units here

See the changelog when upgrading.

Testing

With Python 3.11 or newer and Typst installed, run:

python3 tools/test.py

The runner compiles all test fixtures and examples, checks their assertions and expected-error diagnostics, and tests the local package import against this checkout. It also compares each successful PDF's SHA-256 hash with tests/pdf-hashes.json. New, changed, or removed PDFs require review and give a nonzero exit code. Expected-error fixtures have no PDF baseline.

PDFs use a fixed creation timestamp and only Typst's embedded fonts. The baseline records the exact compiler version; changing it requires reviewing all PDF baselines together. Dependencies use Typst's normal package cache. Use --typst /path/to/typst to select a compiler, or --compile-only to check compilation and diagnostics without comparing or updating hashes.

When a hash changes, open build/pdf-review/review.md and inspect the linked PDFs. The implementing agent can do this review using the task's intended behavior and the test source. Explain why each output change is expected before accepting it; do not accept solely because the implementation changed. A hash detects a difference, but does not establish that either output is correct.

Accept only the fixtures you have reviewed, using repository-relative paths:

python3 tools/test.py --accept tests/calculate.typ examples/all-functions.typ
python3 tools/test.py

Acceptance copies hashes from the last completed review, without recompiling. It checks that the baseline and retained PDFs have not changed and refuses runs with compilation or diagnostic failures. The review is consumed after acceptance; run the tests again before accepting further changes. If source files changed after the review, the next test run checks their new output against the accepted hashes.

For the initial baseline or an intentional compiler upgrade, inspect all outputs and use python3 tools/test.py --accept all. This is an explicit acceptance step, never part of a normal test run. The initial baseline captures existing output; it does not certify every example's visual or mathematical correctness.

Only hashes are tracked in Git. PDFs are cached by hash under the ignored build/pdf-review/pdfs/ directory, so subsequent reviews can link both old and new output without storing reference images. On a fresh checkout, an old PDF may be unavailable: run the tests in a separate checkout of the baseline's Git revision with its recorded compiler version to reproduce it, and verify its hash before comparing. Keep that checkout until the review is complete. Deleting build/pdf-review/ removes this local cache and pending review, but not the tracked baseline.

Run the test runner's own regression tests with python3 -B -m unittest discover -s tools/tests -v. These exercise hash acceptance, retained PDFs, additions and removals, failures, timeouts, and compiler changes in temporary projects. A real PDF smoke test also runs when Typst is available.

Why is it called math-once?

The project started as typst-math, which is still the name of my local folder. Codex later used math-once as the heading in an early README. The name probably came from my original request: make something like eqrun, but let me save variables so I only have to do the calculation once. The name stuck, partly because github.com/kjullu/math-once is now in my browser autocomplete and I still do not have a better one.

License

MIT

About

Made by codex (GPT-5.6 Sol) to do physics calculations in typst.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages