mathr 0.1.7

Rust math library and CLI calculator for symbolic differentiation, integration, FFT, linear algebra (LU, Cholesky, SVD), equation solving, ODE solvers, number theory, special functions, LaTeX input, plotting, and a Jupyter-like web notebook with KaTeX rendering.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# Specification

## CLI Subcommands

| Command | Syntax | Description |
|---------|--------|-------------|
| `eval` | `mathr eval <expr> [--set name=value]` | Evaluate expression |
| `diff` | `mathr diff <expr> [--var x] [--simplify]` | Symbolic derivative (partial if multivariate) |
| `simplify` | `mathr simplify <expr>` | Constant-fold & simplify |
| `integrate` | `mathr integrate <expr> [--var x]` | Symbolic indefinite integral |
| `integrate-num` | `mathr integrate-num <expr> a b [--var x] [--n N] [--adaptive] [--romberg LEVELS]` | Numerical integral |
| `solve` | `mathr solve <expr> [--var x] [--guess 0] [--bisect A B] [--max-iter 100] [--tol 1e-10]` | Root finding |
| `solve-system` | `mathr solve-system <sys> [--guess x0,y0,...]` | Newton's method for nonlinear systems |
| `poly-roots` | `mathr poly-roots <coeffs...>` | Polynomial roots (Durand–Kerner) |
| `isolate-roots` | `mathr isolate-roots <ints...>` | Real root isolation (VAS, integer coefficients) |
| `plot` | `mathr plot <expr> [-o path] [--var x] [--a -τ] [--b τ] [--samples 800]` | PNG plot |
| `fft` | `mathr fft <samples...> [--complex] [--inverse] [--magnitude] [--power]` | FFT |
| `conv` | `mathr conv <a...> <b...>` | FFT convolution of two signals |
| `taylor` | `mathr taylor <expr> [--var x] [--around 0] [--order 5]` | Taylor series |
| `laurent` | `mathr laurent <expr> [a] [pole_order] [n_positive]` | Laurent series around a pole |
| `rat` | `mathr rat <a> <op> <b>` | Exact rational arithmetic |
| `notebook` | `mathr notebook [file.mnb] [port]` | Web notebook UI (Jupyter-like) |
| `fourier` | `mathr fourier <expr> <L> <N> [x]` | Fourier series on [-L, L] with N terms |
| `mc` | `mathr mc <expr> <a> <b> <N> [seed]` | Monte Carlo integral over [a, b] |
| `sample` | `mathr sample <dist> <params...> <N> [seed]` | Random sampling (uniform/normal/exponential) |
| `dist` | `mathr dist <dist> <x> <params...>` | PDF and CDF (normal/exponential) |
| `stats` | `mathr stats <data...>` | Descriptive statistics |
| `matrix` | `mathr matrix <op> <rows...>` | `lu`/`qr`/`cholesky`/`svd`/`eig`/`symlig`/`hessenberg`/`schur`/`rank`/`cond`/`null`/`det`/`solve` |
| `tikhonov` | `mathr tikhonov <rows...> \| <b...> <lambda>` | Tikhonov-regularised solve |
| `interp` | `mathr interp <op> ...` | `lagrange`/`newton`/`spline`/`chebyshev`/`legendre` |
| `gcd` | `mathr gcd <n1> <n2> [...]` | GCD of integers |
| `lcm` | `mathr lcm <n1> <n2> [...]` | LCM of integers |
| `is-prime` | `mathr is-prime <n>` | Primality test |
| `factor` | `mathr factor <n>` | Prime factorization |
| `fib` | `mathr fib <n>` | nth Fibonacci number |
| `binom` | `mathr binom <n> <k>` | Binomial coefficient C(n,k) |
| `fact` | `mathr fact <n>` | Factorial n! |
| `mr-prime` | `mathr mr-prime <n> [--rounds 20]` | Miller–Rabin primality test |
| `jacobi` | `mathr jacobi <a> <n>` | Jacobi symbol (a/n) |
| `cf` | `mathr cf <p> <q>` | Continued fraction of p/q |
| `diophantine` | `mathr diophantine <a> <b> <c>` | Solve a·x + b·y = c |
| `dlog` | `mathr dlog <g> <h> <p>` | Discrete logarithm `g^x ≡ h (mod p)` |
| `special` | `mathr special <op> <x>` | `gamma`/`erf`/`erfc`/`sinc`/`bessel_j0`/`bessel_j1`/`bessel_j` |
| `fast` | `mathr fast <func> <x> [y]` | Chebyshev fast approx (`sin`/`cos`/`tan`/`exp`/`log`/`sqrt`/`pow`) |
| `big` | `mathr big <op> <args>` | Big integer ops for inputs > u64::MAX (`prime`/`factor`/`gcd`/`lcm`/`modpow`/`totient`). `fact`/`fib`/`binom` auto-upgrade on overflow. |
| `ad` | `mathr ad <expr> at <var>=<val>` | Automatic differentiation (dual numbers) — returns `f(x)` and `f'(x)` |
| `ad` | `mathr ad grad <expr> with <var>=<val>,...` | Gradient of a multivariate expression |
| `ad` | `mathr ad jacobian <f1>, <f2>, ... with <var>=<val>,...` | Jacobian matrix of a system |
| `repl` | `mathr repl` | Interactive REPL |

## Expression Grammar

```
expr   := term (('+' | '-') term)*
term   := factor (('*' | '/' | 'mod') factor)*  -- includes implicit multiplication
factor := unary ('!')* ('^' factor)?            -- postfix factorial, then right-assoc power
unary  := ('+' | '-')? atom
atom   := number | ident | ident '(' args ')' | '(' expr ')' | '|' expr '|'
args   := expr (',' expr)*
```

### Implicit Multiplication

`2x` → `2*x`, `3(x+1)` → `3*(x+1)`, `(x)(y)` → `x*y`

### Postfix Factorial

`n!` → `factorial(n)`, e.g. `5!` = 120, `(2+3)!` = 120, `3!^2` = 36

Factorial binds tighter than `^`: `n!^2` = `(n!)^2`

### Absolute Value

`|x|` → `abs(x)`, e.g. `|-5|` = 5, `|sin(pi)|` = 0

### Infix Modulo

`a mod b` → `mod(a, b)`, e.g. `7 mod 3` = 1

### Function-Call Notation

These number-theory functions are available as function calls in expressions:

| Notation | Example | Result |
|----------|---------|--------|
| `gcd(a, b)` | `gcd(12, 8)` | 4 |
| `lcm(a, b)` | `lcm(4, 6)` | 12 |
| `C(n, k)` | `C(5, 2)` | 10 |
| `factorial(n)` | `factorial(5)` | 120 |

### TeX Notation

| TeX | Equivalent | Example |
|-----|------------|---------|
| `\binom{n}{k}` | `C(n, k)` | `\binom{5}{2}` = 10 |
| `\gcd(a, b)` | `gcd(a, b)` | `\gcd(12, 8)` = 4 |
| `\lcm(a, b)` | `lcm(a, b)` | `\lcm(4, 6)` = 12 |
| `\frac{a}{b}` | `a / b` | `\frac{1}{2}` = 0.5 |
| `\sqrt{x}` | `sqrt(x)` | `\sqrt{4}` = 2 |

### MathML

W3C Presentation MathML is supported for both export and import.

**Export** (`mathml <expr>`):
```
mathr> mathml x^2 + 1
<math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><msup><mi>x</mi><mn>2</mn></msup><mo>+</mo><mn>1</mn></mrow></math>
```

**Import** (`mathml import <MathML>`):
```
mathr> mathml import <mfrac><mn>1</mn><mn>2</mn></mfrac>
1/2
```

Supported MathML elements: `<mn>`, `<mi>`, `<mo>`, `<mrow>`, `<mfrac>`, `<msup>`, `<msub>`, `<msqrt>`, `<mroot>`, `<mtext>`, `<mstyle>`, `<mfenced>`, `<math>`

### Serialization

`Expr` can be serialized to and from three interchangeable textual formats via the `serialize` module. All three round-trip: `from_*(to_*(e)).equals(e)`.

**S-expressions** (Lisp-like prefix notation):
```
mathr> serialize sexpr 2*x + 1
(add (mul (num 2) (var x)) (num 1))
mathr> serialize sexpr import (add (mul (num 2) (var x)) (num 1))
2*x + 1
```
Node forms: `(num <n>)`, `(var <name>)`, `(neg <e>)`, `(add|sub|mul|div|pow <a> <b>)`, `(func <name> <arg>...)`. Non-finite numbers use `NaN`, `inf`, `-inf`.

**JSON** (nested objects):
```
mathr> serialize json x^2
{"t":"pow","a":{"t":"var","v":"x"},"b":{"t":"num","v":2}}
mathr> serialize json import {"t":"pow","a":{"t":"var","v":"x"},"b":{"t":"num","v":2}}
x^2
```
Schema: `{"t":"num","v":<n>}` (or `"v":"NaN"|"inf"|"-inf"` for non-finite), `{"t":"var","v":"<name>"}`, `{"t":"neg","e":{...}}`, `{"t":"add|sub|mul|div|pow","a":{...},"b":{...}}`, `{"t":"func","n":"<name>","a":[{...},...]}`.

**RPN** (postfix, space-separated):
```
mathr> serialize rpn 2*x + 1
2 x * 1 +
mathr> serialize rpn import 2 x * 1 +
2*x + 1
```
Operators: `+ - * / ^` (binary), `neg` (unary). Functions use a `<name>:<arity>` call token, e.g. `x sin:1` for `sin(x)`, `x 2 pow:2` for `pow(x, 2)`.

### Interval Arithmetic

Interval arithmetic computes guaranteed bounds on a function's output over a range of inputs. Instead of a single value, each variable is assigned an interval `[lo, hi]`, and operations propagate worst-case bounds through the expression.

**REPL** (`interval <expr> with <var>=[lo,hi],...`):
```
mathr> interval x^2 + 1 with x=[-2,3]
[1, 10]
mathr> interval sin(x) with x=[0,6.283185307179586]
[-1, 1]
mathr> interval x*y with x=[1,2],y=[3,4]
[3, 8]
```

**Limitations**:
- No IEEE 1788 outward rounding — bounds use plain `f64` and may be tight at the last bit. Widen by a small epsilon for safety-critical use.
- **Dependency problem**: `x - x` over `[1, 2]` yields `[-1, 1]`, not `[0, 0]`, because each occurrence of `x` is treated independently. This is fundamental to interval arithmetic.
- Division by an interval containing zero returns the whole real line `[-∞, ∞]`.

**Supported functions**: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `exp`, `ln`, `log`, `log10`, `log2`, `sqrt`, `abs`, `sqr`, `cbrt`, `floor`, `ceil`, `round`, `sign`, `fract`, `min`, `max`, `pow`, `mod`. Trig functions track global extrema (±1) when the input interval spans a peak or trough.

### Arbitrary-Precision Decimals

The `bigdec` module evaluates expressions with exact decimal arithmetic at a chosen number of significant digits (default 30, max 1000). Constants `pi`, `e`, and `tau` are computed at full target precision (not the f64 approximations used by normal evaluation).

**REPL** (`dec <expr> [prec <n>] [with <var>=<val>,...]`):
```
mathr> dec pi prec 50
3.1415926535897932384626433832795028841971693993751
mathr> dec sqrt(2) prec 30
1.414213562373095048801688724210
mathr> dec 1/3 prec 10
0.3333333333
mathr> dec x*2 + 1 with x=1.5 prec 10
4.000000000
```

**Supported functions**: `sqrt`, `cbrt`, `exp`, `ln`, `log`, `log2`, `log10`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `abs`, `floor`, `ceil`, `round`, `sign`, `min`, `max`, `pow(a, b)`, `fact(n)`.

**How it works**: all intermediate steps keep 10 guard digits beyond the requested precision (MPFR-style); π comes from Machin's formula (16·atan(1/5) − 4·atan(1/239)), exp/ln use argument reduction plus Taylor/atanh series, and trig uses π/2 quadrant reduction with Taylor series.

**Limitations**:
- Decimal exponents beyond ±1,000,000 (i.e. 10^±1000000) are rejected.
- Results are correctly rounded to `prec` significant digits but are not interval-certified (no outward rounding).

### Limits

The `limit` module computes two-sided limits `lim x→a f(x)` for finite points and for `x → ±∞`, using a three-stage strategy:

1. **Direct substitution** on the simplified expression (continuity).
2. **L'Hôpital's rule** for quotients in `0/0` or `∞/∞` form — differentiates numerator and denominator and recurses (up to 6 times).
3. **Numeric probing** — evaluates both sides approaching the target; detects finite convergence, poles (`±∞`), opposite-side divergence, and oscillation (`does not exist`).

**REPL** (`limit <expr> [<var>] <point>` — the variable may be omitted when the expression has exactly one):
```
mathr> limit sin(x)/x x 0
lim sin(x)/x as x → 0 = 1
mathr> limit (x^2 - 1)/(x - 1) 1
lim (x^2 - 1)/(x - 1) as x → 1 = 2
mathr> limit 1/x^2 0
lim 1/x^2 as x → 0 = +∞
mathr> limit 1/x 0
lim 1/x as x → 0 = does not exist
mathr> limit (2*x + 1)/(x + 5) inf
lim (2*x + 1)/(x + 5) as x → +∞ = 2
mathr> limit x/exp(x) x inf
lim x/exp(x) as x → +∞ = 0
```

Points: any number, or `inf`/`-inf`. The notebook step-by-step view shows substitution results, L'Hôpital applications, and the final verdict.

**Limitations**:
- Two-sided limits only (no one-sided `x → a⁺` syntax; sides are analysed internally).
- L'Hôpital applies to top-level quotients only; other indeterminate forms (`∞−∞`, `0·∞`, `0^0`) fall through to numeric probing.
- Numeric probing classifies behaviour by sampling; exotic functions may be misclassified near pathological points.

### Polynomial Expansion

The `poly` module distributes products and non-negative integer powers into a collected sum of monomials. Multivariate expressions are supported; like terms are collected; output is ordered by descending degree. Non-polynomial parts (function calls, symbolic powers, variable denominators) are left intact while their polynomial children still distribute.

**REPL** (`expand <expr>`):
```
mathr> expand (x+1)^3
x^3 + 3*x^2 + 3*x + 1
mathr> expand (x+y)*(x-y)
x^2 - y^2
mathr> expand (x+2)*(x+3)
x^2 + 5*x + 6
```

**Limitations**: integer exponents up to 64 are expanded; larger (or symbolic) exponents stay as `Pow`. Division only distributes when the denominator is a non-zero constant. Term count is capped at 20,000.

### Partial Fraction Decomposition

The `apart` module decomposes a rational function `N(x)/D(x)` into a polynomial quotient plus a sum of fractions over the linear and irreducible quadratic factors of `D`:

**REPL** (`apart <expr> [<var>]` — the variable may be omitted when the expression has exactly one):
```
mathr> apart 1/(x*(x+1))
-(1/(x + 1)) + 1/x
mathr> apart (x^2+1)/(x-1)
x + 1 + 2/(x - 1)
mathr> apart 1/(x^3+x^2)
1/(x + 1) - 1/x + 1/x^2
```

**Method**: polynomial long division first (`N = Q·D + R`), then numeric factorization of `D` (Durand–Kerner complex roots; conjugate pairs become irreducible quadratics; repeated roots are clustered for multiplicity), then a square linear system for the unknown coefficients solved by Gaussian elimination. Coefficients near integers are snapped for display.

**Limitations**:
- Univariate with a polynomial denominator only (functions in the denominator, or multiple variables, are rejected).
- Denominator degree is capped at 32; repeated roots carry the numerical fuzz inherent to multiple-root finding (coefficients may be off in the last digits).
- Factorization is numeric, so exact rational coefficients are approximated (typically ~12 significant digits).

### Numbers

- Integers: `42`
- Decimals: `3.14`
- Scientific: `1.5e3`, `2E-2`

## Constants

| Name | Value |
|------|-------|
| `pi`, `PI` | π ≈ 3.14159... |
| `e` | e ≈ 2.71828... |
| `tau` | τ = 2π |
| `inf`, `Inf`, `Infinity` | +∞ |
| `nan`, `NaN` | NaN |

## Built-in Functions

| Category | Functions |
|----------|-----------|
| Trig | `sin`, `cos`, `tan`, `asin`, `acos`, `atan` |
| Hyperbolic | `sinh`, `cosh`, `tanh` |
| Exp/Log | `exp`, `ln`, `log(x,b)`, `log2`, `log10` |
| Roots | `sqrt`, `cbrt` |
| Rounding | `floor`, `ceil`, `round`, `fract` |
| Other | `abs`, `sign`, `min(...)`, `max(...)`, `pow(x,y)`, `mod(x,y)` |
| Special | `gamma`, `erf`, `erfc`, `sinc`, `bessel_j0`, `bessel_j1`, `bessel_j(n,x)`, `digamma`, `trigamma`, `polygamma(m,x)`, `harmonic(n)`, `zeta(s)`, `hurwitz(s,a)`, `elliptic_k(k)`, `elliptic_e(k)`, `elliptic_f(phi,k)`, `elliptic_e_inc(phi,k)` |

## REPL Commands

| Command | Description |
|---------|-------------|
| `<expr>` | Evaluate |
| `let x = <expr>` | Bind variable |
| `fn f(x) = <expr>` | Define function |
| `diff <expr> [var]` | Symbolic derivative |
| `pdiff <expr> <var>` | Partial derivative |
| `gradient <expr>` | Gradient (all partials) |
| `integrate <expr> [var]` | Symbolic integration |
| `simplify <expr>` | Simplify |
| `int <expr> a b` | Numerical integral |
| `romberg <expr> a b` | Romberg-integrated |
| `solve <expr> [var] [guess]` | Root finding |
| `plot <expr> a b [out.png]` | PNG plot |
| `taylor <expr> [a] [order]` | Taylor series |
| `laurent <expr> [a] [k] [N]` | Laurent series around a pole |
| `rat <a> <op> <b>` | Exact rational arithmetic |
| `fourier <expr> L N [x]` | Fourier series on [-L, L] |
| `mc <expr> a b N [seed]` | Monte Carlo integral |
| `sample <dist> <params...> N [seed]` | Random sampling |
| `dist <dist> <x> <params...>` | PDF and CDF |
| `fft <numbers...>` | Magnitude spectrum |
| `conv <a...> x <b...>` | Convolution |
| `stats <numbers...>` | Descriptive statistics |
| `poly-roots <coeffs...>` | Polynomial roots |
| `isolate-roots <ints...>` | Real root isolation (VAS) |
| `lu <rows...>` | LU decomposition (rows separated by `\|`) |
| `qr <rows...>` | QR decomposition (Householder reflections; prints Q and R) |
| `tikhonov <rows...> \| <b...> <lambda>` | Tikhonov-regularised solve |
| `cholesky <rows...>` | Cholesky decomposition |
| `svd <rows...>` | Singular value decomposition |
| `eig <rows...>` | Dominant eigenpair (power iteration) |
| `symlig <rows...>` | Full symmetric eigenvalue decomposition (QR algorithm) |
| `hessenberg <rows...>` | Hessenberg decomposition `A = Q·H·Qᵀ` |
| `schur <rows...>` | Real Schur decomposition `A = Q·T·Qᵀ` |
| `rank <rows...>` | Matrix rank |
| `cond <rows...>` | 2-norm condition number σ_max/σ_min (SVD); `inf` for singular |
| `null <rows...>` | Orthonormal nullspace basis of `{x : A·x = 0}` |
| `det <rows...>` | Matrix determinant |
| `spline x1 y1 x2 y2 ... x_at` | Cubic spline at `x_at` |
| `chebyshev n [x]` | Chebyshev `T_n(x)` (or `n` nodes) |
| `legendre n [x]` | Legendre `P_n(x)` (or `n`-point Gauss–Legendre) |
| `gcd / lcm / is-prime / factor / fib / binom / fact / mr-prime` | Number theory |
| `jacobi <a> <n>` | Jacobi symbol |
| `cf <p> <q>` | Continued fraction |
| `diophantine <a> <b> <c>` | Linear Diophantine solver |
| `dlog <g> <h> <p>` | Discrete logarithm |
| `mathml <expr>` / `mathml import <ml>` | Presentation MathML export/import |
| `serialize <fmt> <expr>` / `serialize <fmt> import <t>` | Expression serialization (`fmt`: `sexpr`/`json`/`rpn`) |
| `interval <expr> with <var>=[lo,hi],...` | Rigorous bounds via interval arithmetic |
| `vars` / `funcs` | Show bindings |
| `clear` | Reset context |
| `help` | Help text |
| `quit` | Exit |

## Notebook File Format (`.mnb`)

A math notebook is a JSON file with a `cells` array. Each cell has an `id`, `input` (math expression or TeX), `output` (evaluation result), and `cell_type` (`"math"` or `"text"`):

```json
{
  "cells": [
    { "id": 0, "input": "let x = 5", "output": "x = 5", "cell_type": "math" },
    { "id": 1, "input": "x * 3", "output": "= 15", "cell_type": "math" },
    { "id": 2, "input": "# Quadratic formula", "output": "# Quadratic formula", "cell_type": "text" }
  ]
}
```

The `cell_type` field is optional in old `.mnb` files (defaults to `"math"`).

Start the web UI with `mathr notebook [file.mnb] [port]` (default port 3000).

### Web UI Features

- **Shared context** — variables and functions defined via `let`/`fn` in one cell persist across subsequent cells (like Jupyter kernels)
- **Cell types** — Math cells (evaluated, KaTeX-rendered) and Text cells (Markdown-rendered documentation)
- **Inline plots**`plot` commands render PNG images directly in the notebook via base64-encoded responses
- **Cell management** — add, delete, duplicate, move up/down, toggle type
- **Execution status** — each cell shows running/done/error status with `In [n]:` execution counters
- **Context panel** — collapsible panel showing bound variables and user functions
- **Reset & Run All** — resets the shared context and re-evaluates all cells in order
- **Markdown rendering** — text cells render Markdown (headings, lists, code, blockquotes) via marked.js
- **KaTeX rendering** — input expressions and output results are rendered as math notation
- **Step-by-step solving**`POST /api/eval` returns a `steps` array with intermediate steps for `diff`, `solve`, `taylor`, `integrate`, `simplify`, `rat`, `laurent`
- **Exact fraction arithmetic** — expressions with integer fractions (e.g. `\frac{1}{2} + \frac{3}{4}`) are evaluated exactly as `Rational`, returning `5/4` instead of `1.25`
- **Live input preview** — each cell shows a rendered math/Markdown preview as you type
- **Keyboard shortcuts** — Shift/Cmd/Ctrl+Enter to run a cell, Alt+Enter to run and add a new cell

### API Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/` | Serve web UI HTML |
| `POST` | `/api/eval` | Evaluate expression (updates shared context); returns `{input, output, steps, image?}` where `image` is base64 PNG for `plot` commands |
| `GET` | `/api/notebook` | Get current notebook as JSON |
| `POST` | `/api/notebook` | Replace notebook state (auto-saves to file) |
| `POST` | `/api/save` | Save notebook to file |
| `POST` | `/api/reset` | Reset the shared evaluation context |
| `GET` | `/api/context` | Get current variables and user functions as `{vars, funcs}` |

## Error Handling

All library functions return `Result<T, MathError>` with variants:
- `Parse` — syntax errors
- `Eval` — evaluation errors (wrong arg count, etc.)
- `UnknownVariable` / `UnknownFunction`
- `Domain` — domain errors (sqrt of negative, log of 0)
- `NotConvergent` — solver/ODE failed to converge
- `InvalidArgument` — bad input dimensions/values
- `Io` — I/O errors
- `Plot` — rendering errors
- `Other` — catch-all