macro librarywork/xetal-libraries/libs/Equations/src/Equations.xtlm

Equations: its macro library -- e_q<, t_ex< and b_oth<: an equation as printed in a paper, to its value, its LaTeX, or both. Imported with the functions, under one alias: "e:" u_se< "Equations". A macro is a function from the source text written left and right of its call to the source that replaces the call, run before the program is compiled (X_eTaL MC10). Names with m: are macros; those under h: are private to this file.

source · imports t: work/xetal-libraries/libs/Strings/src/Strings.xtl; pa: work/xetal-libraries/libs/Parsing/src/Parsing.xtl

The macros

ᵐe̲q< : Char -> Char -> Char

macro · line 46

The value of an equation written as a paper prints it. The left names the result, its free indices and their extents ("Y[o,h,w] | O Ho Wo", or "L[] | " for a scalar). The right is an expression over the free indices in which a parenthesized sum is a term: "(sum j:=0..M-1 | W[i,j] * x[j]) + b[i]" A sum is an operator word (sum, prod, max, min), an index, := and an inclusive range low..high, then | and its body; several, separated by commas, nest outermost first. A right side that is one bare sum needs no parentheses: "sum c:=0..C-1, sum r:=0..Kh-1 | X[c,h+r] * W[c,r]". The word integral binds a continuous variable over Float bounds and takes the value of its body by a quadrature rule written after by: "integral tau:=0..1 by simpson 64 | exp(tau)" (trapezoid or simpson, then the number of steps; without by, trapezoid 64; simpson needs an even number). The variable is the grid of n + 1 points a, a + h, ..., b; a bound of inf is refused (write a truncation and show the tail is small, or change variables). Trapezoid is exact on lines, simpson on cubics; at a jump of the body the sample takes the body's value at that point as written. Expressions follow math precedence: ^ binds tightest (right to left), then * and /, then + and - (left to right); parentheses group; the functions are exp(x), log(x), sqrt(x), abs(x), sin(x), cos(x), max(a, b) and min(a, b); A[e1,e2] reads one element of the caller's array A, one index per axis, from 0. Every reading rule uses all of its text or refuses the call, and what was read is written back and compared with the text at every call, so nothing written is ever dropped. Both sides are notations (X_eTaL MC32): where a program is drawn (the pages, the live demo, xetal render --html) the call's two strings are shown as one math box, what e:t_ex< gives for them, after the macro's name; the value and the text are unchanged. draws: t_ex<

ᵐe̲q< ← { lhs rhs → (ʰf̲ree lhs) ʰv̲alue ʰc̲hecked rhs }
Used in: y, step, y

ᵐt̲ex< : Char -> Char -> Char

macro · line 55

The LaTeX of the same call, a string: the result as written (L for a scalar, Y_{o,h,w} for an array), then = and the expression, with \sum \prod \max \min, \int_{a}^{b} ... \, d\tau for an integral (a Greek letter's name as its command), \frac for a quotient, \sqrt \exp \log \sin \cos, and the brackets as subscripts.

ᵐt̲ex< ← { lhs rhs → "\"" c̲at ((0 ʰt̲ex lhs)_ ʰc̲hecked rhs) c̲at "\"" }

ᵐt̲exSymbols< : Char -> Char -> Char

macro · line 68

The same LaTeX with each symbol the caller supplies marked for a page: the result, every array reference with its subscripts and every named scalar (in a body or a range bound) wrapped in KaTeX's \htmlClass{sym sym-NAME}{...}, NAME as written (a Greek letter by its word: sym-tau). Indices, an integral's variable, prev, functions and numbers are not wrapped (the classes idx and idx-NAME are kept for indices). Everything else is e:t_ex<'s output, unchanged. KaTeX draws \htmlClass only with trust allowing it and strict letting the htmlExtension through.

ᵐt̲exSymbols< ← { lhs rhs → "\"" c̲at ((1 ʰt̲ex lhs)_ ʰc̲hecked rhs) c̲at "\"" }

ᵐb̲oth< : Char -> Char -> Char

macro · line 74

Both from one call: the pair (value, LaTeX); drawn as e:e_q< is. draws: t_ex<

ᵐb̲oth< ← { lhs rhs → "(" c̲at ((ʰf̲ree lhs) ʰv̲alue ʰc̲hecked rhs) c̲at ", \"" c̲at ((0 ʰt̲ex lhs)_ ʰc̲hecked rhs) c̲at "\")" }

ᵐc̲onvolve< : Char -> Char -> Char

macro · line 93

Discrete 1-D convolution and cross-correlation, written as the sums they are (X_eTaL Saga 42 decision 5: library macros over sum, not words of the grammar). The left names the result, its index and the boundary mode, "y[n] | full"; the right names the signal and the kernel, two of the caller's vectors, "x h". The modes: full gives L + M - 1 values (every overlap, the signal padded with M - 1 zeros at each end), same the L centered on full (as numpy centers them), valid the L - M + 1 complete overlaps (a kernel longer than the signal stops the program when it runs); no mode is implicit. Convolution is y[n] = sum over m of x[n - m] h[m], the kernel reversed as it slides; correlation y[n] = sum over m of x[n + m] h[m], not reversed, so correlation is convolution with the kernel reversed. xetal expand shows the sum each call becomes. Both sides are notations (MC32), drawn by e:c_onvolveTex<. draws: c_onvolveTex<

ᵐc̲onvolve< ← { lhs rhs → "convolve" ʰs̲lide (lhs, rhs) }

ᵐc̲orrelate< : Char -> Char -> Char

macro · line 100

Cross-correlation, the same call shape (see c_onvolve<); drawn by e:c_orrelateTex<. draws: c_orrelateTex<

ᵐc̲orrelate< ← { lhs rhs → "correlate" ʰs̲lide (lhs, rhs) }

ᵐc̲onvolveTex< : Char -> Char -> Char

macro · line 106

The LaTeX of a convolution call, a string: the textbook sum, y_{n} = \sum_{m} x_{n-m} \, h_{m}, with the mode after it in words.

ᵐc̲onvolveTex< ← { lhs rhs → "convolve" ʰs̲lideTex (lhs, rhs) }

ᵐc̲orrelateTex< : Char -> Char -> Char

macro · line 111

The LaTeX of a correlation call (x_{n+m}, the kernel not reversed).

ᵐc̲orrelateTex< ← { lhs rhs → "correlate" ʰs̲lideTex (lhs, rhs) }

Convolution and correlation

modes : Box Char

value (private) · line 116

The boundary modes.

modes ← "full" "same" "valid"
Used in: ʰs̲lideLeft

ʰs̲lide : Char -> (Char, Char) -> Char

function (private) · line 123

The expansion of a convolution or correlation call: a block that measures the signal (L) and the kernel (M), pads the signal with M - 1 zeros of its own type at each end (full and same), checks the lengths (valid), then the equation over the free index, computed as any equation is.

ʰs̲lide ← { op (lhs, rhs) →
  (y, n, mode) ← ʰs̲lideLeft lhs
  (x, h) ← ʰs̲lideRight rhs
  (gL, gM, gC, gP) ← (⎕G̲ENSYM "L", ⎕G̲ENSYM "M", ⎕G̲ENSYM "c", ⎕G̲ENSYM "p")
  m ← n ʰo̲therIndex ("m", "j", "q")
  lines ← "  " c̲at gL c̲at " := t_ally " c̲at x c̲at "\n  " c̲at gM c̲at " := t_ally " c̲at h c̲at "\n  " c̲at gC c̲at " := (" c̲at gM c̲at " - 1) d_iv 2\n  " c̲at gP c̲at " := ((" c̲at gM c̲at " - 1) r_eshape 0 * 1 t_ake " c̲at x c̲at ") c_at " c̲at x c̲at " c_at (" c̲at gM c̲at " - 1) r_eshape 0 * 1 t_ake " c̲at x c̲at "\n"
  check ← ("valid" m̲atch mode) ʰp̲ickText ("  _ := { @ -> " c̲at gM c̲at " <= " c̲at gL c̲at " ? @; \"valid-mode\" []S_IGNAL \"valid: the kernel (\" c_at (f_ormat " c̲at gM c̲at ") c_at \") is longer than the signal (\" c_at (f_ormat " c̲at gL c̲at ") c_at \")\" } @\n", "")
  extent ← ("full" m̲atch mode) ʰp̲ickText (gL c̲at "+" c̲at gM c̲at "-1", ("same" m̲atch mode) ʰp̲ickText (gL, gL c̲at "-" c̲at gM c̲at "+1"))
  signal ← ("valid" m̲atch mode) ʰp̲ickText (x, gP)
  shift ← ("full" m̲atch mode) ʰp̲ickText (gM c̲at "-1", ("same" m̲atch mode) ʰp̲ickText (gM c̲at "-1+" c̲at gC, ""))
  at ← ("convolve" m̲atch op) ʰp̲ickText (n c̲at (ʰp̲lus ("valid" m̲atch mode) ʰp̲ickText (gM c̲at "-1", shift)) c̲at "-" c̲at m, n c̲at (ʰp̲lus ("valid" m̲atch mode) ʰp̲ickText ("", ("full" m̲atch mode) ʰp̲ickText ("", gC))) c̲at "+" c̲at m)
  eq ← "sum " c̲at m c̲at ":=0.." c̲at gM c̲at "-1 | " c̲at signal c̲at "[" c̲at at c̲at "] * " c̲at h c̲at "[" c̲at m c̲at "]"
  "({ @ ->\n" c̲at lines c̲at check c̲at "  " c̲at ((ʰf̲ree y c̲at "[" c̲at n c̲at "] | " c̲at extent) ʰv̲alue ʰc̲hecked eq) c̲at "\n} @)"
}

ʰs̲lideTex : Char -> (Char, Char) -> Char

function (private) · line 142

The LaTeX of a convolution or correlation call as a string literal: the result and its index, = , the sum over the kernel's index of the signal at n - m (convolution) or n + m (correlation) times the kernel at m, then the mode in words; the sides read as the call is.

ʰs̲lideTex ← { op (lhs, rhs) →
  (y, n, mode) ← ʰs̲lideLeft lhs
  (x, h) ← ʰs̲lideRight rhs
  m ← n ʰo̲therIndex ("m", "j", "q")
  sign ← ("convolve" m̲atch op) ʰp̲ickText (" - ", " + ")
  "\"" c̲at y c̲at "_{" c̲at n c̲at "} = \\\\sum_{" c̲at m c̲at "} " c̲at x c̲at "_{" c̲at n c̲at sign c̲at m c̲at "} \\\\, " c̲at h c̲at "_{" c̲at m c̲at "} \\\\quad \\\\text{(" c̲at mode c̲at ")}\""
}

ʰp̲lus : Char -> Char

function (private) · line 151

"+" and t, or "" for an empty t.

ʰp̲lus ← { t →
  0 = t̲ally t ? ""
  "+" c̲at t
}
Used in: ʰs̲lide

ʰs̲lideLeft : Char -> (Char, Char, Char)

function (private) · line 158

The left side "y[n] | mode" as (result, index, mode): one index, a mode the library has.

ʰs̲lideLeft ← { lhs →
  l ← "|" ᵖᵃs̲plit lhs
  _ ← (2 = t̲ally l) ᵖᵃr̲equire ("bad-convolution left", "the result, its index and the mode: \"y[n] | full\"")
  (y, ix) ← "bad-convolution left" ᵖᵃr̲eference d̲isclose f̲irst l
  _ ← (1 = t̲ally ix) ᵖᵃr̲equire ("bad-convolution left", "one index: \"y[n] | full\", not " c̲at lhs)
  mode ← ᵗt̲rim d̲isclose 2 s̲elect l
  _ ← (0 < t̲ally mode) ᵖᵃr̲equire ("bad-convolution left", "the mode is not implicit: write full, same or valid after |")
  _ ← ('∨ r̲/ 0 + '{ w → (d̲isclose w) m̲atch mode } e̲ach modes) ᵖᵃr̲equire ("bad-convolution left", "the mode is full, same or valid, not " c̲at mode)
  (y, d̲isclose f̲irst ix, mode)
}

ʰs̲lideRight : Char -> (Char, Char)

function (private) · line 170

The right side "x h" as (signal, kernel): two names.

ʰs̲lideRight ← { rhs →
  ws ← ᵗw̲ords rhs
  _ ← (2 = t̲ally ws) ᵖᵃr̲equire ("bad-convolution right", "the signal and the kernel, two names: \"x h\", not " c̲at rhs)
  _ ← ('∧ r̲/ 1 c̲at '{ w → ᵖᵃn̲ame? d̲isclose w } e̲ach ws) ᵖᵃr̲equire ("bad-convolution right", "the signal and the kernel are names: " c̲at rhs)
  (d̲isclose f̲irst ws, d̲isclose 2 s̲elect ws)
}

ʰo̲therIndex : Char -> (Char, Char, Char) -> Char

function (private) · line 179

The first of the candidates that is not the free index, as the sum's index.

ʰo̲therIndex ← { n (a, b, c) →
  n̲ot n m̲atch a ? a
  n̲ot n m̲atch b ? b
  c
}

Reading the call

ʰf̲ree : Char -> (Box Char, Box Char)

function (private) · line 189

The free indices and their extents, from "Y[o,h,w] | O Ho Wo", as (names, extents), boxed texts; "L[] | " gives none of either.

ʰf̲ree ← { lhs →
  l ← "|" ᵖᵃs̲plit lhs
  _ ← (2 = t̲ally l) ᵖᵃr̲equire ("bad-equation left", "the result, then | and its extents: \"Y[o,h,w] | O Ho Wo\"")
  head ← d̲isclose f̲irst l
  _ ← (0 < t̲ally w̲here head m̲ember? "[") ᵖᵃr̲equire ("bad-equation left", "the result names its free indices in brackets: Y[o,h,w], or L[] for a scalar")
  (_, names) ← "bad-equation left" ᵖᵃr̲eference head
  extents ← " " ᵖᵃs̲plit d̲isclose f̲irst 1 d̲rop l
  _ ← ((t̲ally names) = t̲ally extents) ᵖᵃr̲equire ("bad-equation left", "one extent for each free index: " c̲at lhs)
  (names, extents)
}

ʰc̲hecked : Char -> Char

function (private) · line 204

The right text, checked: its characters are the grammar's, a bare sum is wrapped as the term it is, and the reading written back equals the text with its spaces removed (so no rule dropped a character); the text to read.

ʰc̲hecked ← { rhs →
  t ← ᵗt̲rim rhs
  _ ← (0 < t̲ally t) ᵖᵃr̲equire ("bad-equation right", "an empty equation")
  bad ← (n̲ot t m̲ember? alphabet) r̲eplicate t
  0 < t̲ally bad ? "bad-equation right" ⎕R̲EJECT "not a character of an equation (names, numbers, + - * / ^, parentheses, brackets, a sum's | := and ..): " c̲at (1 t̲ake bad) c̲at " in " c̲at t
  whole ← ʰw̲hole t
  back ← ʰp̲lain whole
  _ ← (back m̲atch ᵖᵃs̲queeze whole) ᵖᵃr̲equire ("bad-equation right", "the reader did not use all of the text: it read " c̲at back c̲at " from " c̲at t)
  t
}

ʰw̲hole : Char -> Char

function (private) · line 217

A right side that is one bare sum, "sum i:=0..n | body", as the term it is, parenthesized; anything else as written.

ʰw̲hole ← { t →
  0 < '+ r̲/ 0 + "|" ᵖᵃt̲op t ? "(" c̲at t c̲at ")"
  t
}

The grammar, shared by the three readings

nameChars : Char

value (private) · line 225

Characters of names and numbers.

nameChars ← "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:"

alphabet : Char

value (private) · line 228

Every character an equation may hold.

alphabet ← nameChars c̲at ".+-*/^()[]|,= "
Used in: ʰc̲hecked

words : Box Char

value (private) · line 231

The operator words and the functions.

words ← "sum" "prod" "max" "min" "integral" "scan"
Used in: ʰb̲inding

functions : Box Char

value (private) · line 232
functions ← "exp" "log" "sqrt" "abs" "sin" "cos" "max" "min"

rules : Box Char

value (private) · line 236

The quadrature rules an integral may be taken by, and the default option text when a call writes none.

rules ← "trapezoid" "simpson"
Used in: ʰo̲ption?

defaultBy : Char

value (private) · line 237
defaultBy ← "trapezoid 64"
Used in: ʰr̲uleSteps

infinities : Box Char

value (private) · line 240

The spellings of an infinite bound.

infinities ← "inf" "-inf" "+inf" "infinity" "-infinity" "oo" "-oo"
Used in: ʰi̲nfinite?

ʰp̲ickText : Truthy a => a -> (Char, Char) -> Char

function (private) · line 244

a when c holds, else b, both already computed: texts or counts (one each: a library's helpers form one binding group, in which a helper used at two types is not yet generalized).

ʰp̲ickText ← { c (a, b) →
  c ? a
  b
}

ʰp̲ickCount : Truthy a => a -> (Int, Int) -> Int

function (private) · line 248
ʰp̲ickCount ← { c (a, b) →
  c ? a
  b
}
Used in: ʰf̲nParts

ʰk̲ind : Char -> Char

function (private) · line 258

What a term is, from its text (trimmed, not empty): "add" (a chain of + and -, a leading - a negation), "mul" (* and /), "pow" (^), "sum" (a parenthesized sum), "group" (parentheses), "fn" (a function applied), "ref" (an array indexed) or "atom" (a name or a number). One place decides, so the value, the LaTeX and the reprint agree.

ʰk̲ind ← { t →
  0 < '+ r̲/ 0 + "+-" ᵖᵃt̲op t ? "add"
  0 < '+ r̲/ 0 + "*/" ᵖᵃt̲op t ? "mul"
  0 < '+ r̲/ 0 + "^" ᵖᵃt̲op t ? "pow"
  (ᵖᵃg̲roup? t) ∧ 0 < '+ r̲/ 0 + "|" ᵖᵃt̲op ᵖᵃi̲nner t ? "sum"
  ᵖᵃg̲roup? t ? "group"
  ʰf̲n? t ? "fn"
  0 < t̲ally w̲here t m̲ember? "[" ? "ref"
  "atom"
}

ʰf̲n? : Truthy a => Char -> a

function (private) · line 273

Whether t is a function applied: a word, then one parenthesized group that ends the text (pa:a_pplied?'s shape alone): the fixed math functions and the program's own are both read this way (X_eTaL-ML ask E3), told apart only in h:v_alueFn/h:t_exFn.

ʰf̲n? ← { t → ᵖᵃa̲pplied? t }
Used in: ʰk̲ind

ʰf̲nParts : Char -> (Char, Box Char)

function (private) · line 281

A function applied, as (name, arguments), the arguments cut at the top-level commas. The count is checked only for a fixed math function (its own, not the caller's): a named function is trusted with however many it is given, since nothing here knows its real arity -- a wrong count is caught only when the expanded code fails to compile (X_eTaL-ML ask E3, decided with the user).

ʰf̲nParts ← { t →
  (name, args) ← "bad-equation right" ᵖᵃa̲pplied t
  known ← 0 < '+ r̲/ 0 + '{ f → (d̲isclose f) m̲atch name } e̲ach functions
  wanted ← (("max" m̲atch name) ∨ ("min" m̲atch name)) ʰp̲ickCount (2, 1)
  _ ← ((n̲ot known) ∨ (wanted = t̲ally args)) ᵖᵃr̲equire ("bad-equation right", name c̲at " takes " c̲at (f̲ormat wanted) c̲at " argument(s): " c̲at t)
  (name, args)
}

ʰs̲umParts : Char -> (Box Char, Box Char, Box Char, Box Char, Box Char, Char)

function (private) · line 293

A parenthesized sum as (words, indices, lows, highs, options, body): the bindings before the top-level |, each "word i:=low..high" (the word an operator word), an integral's followed by its by options (the text after by as written, "" for none).

ʰs̲umParts ← { t →
  inner ← ᵖᵃi̲nner t
  cut ← "|" ᵖᵃt̲op inner
  _ ← (1 = '+ r̲/ 0 + cut) ᵖᵃr̲equire ("bad-equation right", "a sum has its bindings, then one | and its body: " c̲at t)
  at ← f̲irst w̲here cut
  bindings ← "," ᵖᵃs̲plit (at − 1) t̲ake inner
  body ← ᵗt̲rim at d̲rop inner
  _ ← (0 < t̲ally body) ᵖᵃr̲equire ("bad-equation right", "a sum needs a body after |: " c̲at t)
  ds ← '{ b → ʰb̲oxed ʰb̲inding d̲isclose b } m̲ap bindings
  p̲art ← { k → '{ d → d̲isclose k s̲elect d̲isclose d } m̲ap ds }
  (p̲art 1, p̲art 2, p̲art 3, p̲art 4, p̲art 5, body)
}

ʰb̲inding : Char -> (Char, Char, Char, Char, Char)

function (private) · line 312

One binding, "word i:=low..high", "integral x:=a..b by rule n" or "scan t:=lo..hi by seed", as (word, index, low, high, options): the by options cut off before Parsing reads the range, then checked (an integral's rule and grid; a scan's seed, any expression of the caller's values, checked only when it is read as one; a sum has neither; an integral's bounds are finite).

ʰb̲inding ← { b →
  at ← " by " ᵖᵃa̲t b
  core ← ᵗt̲rim at ʰu̲pTo b
  opt ← ᵗt̲rim (at, 4) ʰp̲ast b
  (w, i, lo, hi) ← ("bad-equation right", "an operator word", words) ᵖᵃb̲inding core
  integral ← "integral" m̲atch w
  scan ← "scan" m̲atch w
  _ ← ((integral ∨ scan) ∨ 0 = at) ᵖᵃr̲equire ("bad-equation right", "by names an integral's rule and grid, or a scan's seed; a " c̲at w c̲at " takes none: " c̲at b)
  _ ← (n̲ot integral ∧ (ʰi̲nfinite? lo) ∨ ʰi̲nfinite? hi) ᵖᵃr̲equire ("bad-equation right", "an improper integral (a bound of inf): write a truncation, 0..20 say, and show the tail is small, or change variables so the bounds are finite: " c̲at b)
  _ ← ʰo̲ptionsOk (integral, at, opt)
  (w, i, lo, hi, opt)
}
Used in: ʰs̲umParts

ʰi̲nfinite? : (Num a, Truthy a) => Char -> a

function (private) · line 326

Whether a bound is written infinite (inf, -inf, infinity, oo).

ʰi̲nfinite? ← { b → '∨ r̲/ 0 + '{ f → (d̲isclose f) m̲atch b } e̲ach infinities }
Used in: ʰb̲inding

ʰo̲ptionsOk : (Truthy a, Truthy b) => (a, Int, Char) -> b

function (private) · line 331

The by options checked when there are any (at is where by was found, 0 for none): an integral's rule and grid; a scan's seed is any expression, read when the scan itself is (h:v_alueScan).

ʰo̲ptionsOk ← { (integral, at, opt) →
  0 = at ? 1 = 1
  n̲ot integral ? 1 = 1
  ʰo̲ption? opt
}
Used in: ʰb̲inding

ʰo̲ption? : Truthy a => Char -> a

function (private) · line 339

Whether an integral's by options are a rule the library has and a whole number of steps (even for simpson); else a refusal.

ʰo̲ption? ← { opt →
  ws ← ᵗw̲ords opt
  _ ← (2 = t̲ally ws) ᵖᵃr̲equire ("bad-equation right", "after by: the rule and the grid, trapezoid 64 or simpson 32, not " c̲at opt)
  rule ← d̲isclose f̲irst ws
  n ← d̲isclose 2 s̲elect ws
  _ ← ('∨ r̲/ 0 + '{ r → (d̲isclose r) m̲atch rule } e̲ach rules) ᵖᵃr̲equire ("bad-equation right", "the rule is trapezoid or simpson, not " c̲at rule)
  _ ← ((0 < t̲ally n) ∧ '∧ r̲/ n m̲ember? ⎕D) ᵖᵃr̲equire ("bad-equation right", "the grid is a whole number of steps: " c̲at n)
  k ← f̲irst f̲loor n̲umbers n
  _ ← (0 < k) ᵖᵃr̲equire ("bad-equation right", "the grid needs at least one step: " c̲at n)
  _ ← (n̲ot (rule m̲atch "simpson") ∧ 1 = k m̲od 2) ᵖᵃr̲equire ("bad-equation right", "simpson needs an even number of steps, not " c̲at n)
  1 = 1
}
Used in: ʰo̲ptionsOk

ʰr̲uleSteps : Char -> (Char, Int)

function (private) · line 353

The rule and the steps of an integral's options ("" for the default).

ʰr̲uleSteps ← { opt →
  ws ← ᵗw̲ords ((0 = t̲ally opt) ʰp̲ickText (defaultBy, opt))
  (d̲isclose f̲irst ws, f̲irst f̲loor n̲umbers d̲isclose 2 s̲elect ws)
}

ʰu̲pTo : Int -> Char -> Char

function (private) · line 360

s before position k (all of s when k is 0), and s after a separator of length m at position k ("" when k is 0).

ʰu̲pTo ← { k s →
  0 = k ? s
  (k − 1) t̲ake s
}
Used in: ʰb̲inding

ʰp̲ast : (Int, Int) -> Char -> Char

function (private) · line 364
ʰp̲ast ← { (k, m) s →
  0 = k ? ""
  ((k + m) − 1) d̲rop s
}
Used in: ʰb̲inding

ʰb̲oxed : (Char, Char, Char, Char, Char) -> Box Char

function (private) · line 371

A binding's (word, index, low, high, options), boxed, so a sum's bindings can be taken apart by position.

ʰb̲oxed ← { (w, i, lo, hi, o) → (e̲nclose w) c̲at (e̲nclose i) c̲at (e̲nclose lo) c̲at (e̲nclose hi) c̲at e̲nclose o }
Used in: ʰs̲umParts

ʰa̲tom : Char -> Char

function (private) · line 374

An atom checked: a name or a number.

ʰa̲tom ← { t →
  _ ← ((0 < t̲ally t) ∧ '× r̲/ 0 + t m̲ember? nameChars c̲at ".") ᵖᵃr̲equire ("bad-equation right", "not a name, number, A[i,j], function or (sum ...) in an equation: " c̲at t)
  t
}

The value

ʰv̲alue : (Box Char, Box Char) -> Char -> Char

function (private) · line 383

The whole expansion: a block defining the checks, the grid of the free indices, and the expression's value over it.

ʰv̲alue ← { (names, extents) t →
  helpers ← (⎕G̲ENSYM "a_t", ⎕G̲ENSYM "c_heck", ⎕G̲ENSYM "r_ank")
  (at, chk, rk) ← helpers
  grid ← ⎕G̲ENSYM "grid"
  cell ← ⎕G̲ENSYM "g_rid"
  spans ← '{ e → (e̲nclose "") ʰb̲ound d̲isclose e } m̲ap extents
  lows ← (t̲ally names) r̲eshape e̲nclose "(0 + "
  env ← (names, spans, lows, cell, helpers)
  gridLines ← (0 = t̲ally names) ʰp̲ickText ("", ʰg̲ridLines (grid, cell, spans))
  atLine ← "  " c̲at at c̲at " := { A i -> (1 + i) s_elect r_avel A }\n"
  ⍝ A value that names no index is a scalar: shaped to the grid, so the
  ⍝ result has one axis per free index whatever the expression.
  shaped ← (0 = t̲ally names) ʰp̲ickText ("", grid c̲at " r_eshape ")
  "({ @ ->\n" c̲at atLine c̲at (ʰc̲hecks (chk, rk)) c̲at gridLines c̲at "  " c̲at shaped c̲at "(" c̲at (env ʰv̲alueOf ʰw̲hole t) c̲at ")\n} @)"
}

ʰg̲ridLines : (Char, Char, Box Char) -> Char

function (private) · line 401

The lines defining a grid over spans and its cell function (the coordinates along axis k, shaped like the grid).

ʰg̲ridLines ← { (grid, cell, spans) →
  "  " c̲at grid c̲at " := " c̲at (" c_at " ʰj̲oinWith spans) c̲at "\n  " c̲at cell c̲at " := { k -> " c̲at grid c̲at " r_eshape (o_ffsets k s_elect " c̲at grid c̲at ") 'l_eft t_able ('* r_/ k d_rop " c̲at grid c̲at ") r_eshape 0 }\n"
}

ʰv̲alueOf : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> Char -> Char

function (private) · line 407

The value of a term in env (the indices in scope, their spans and lows, the grid's cell function, the helpers), as X_eTaL text.

ʰv̲alueOf ← { env text →
  t ← ᵗt̲rim text
  k ← ʰk̲ind t
  "add" m̲atch k ? env ʰv̲alueChain ("+-", t)
  "mul" m̲atch k ? env ʰv̲alueChain ("*/", t)
  "pow" m̲atch k ? env ʰv̲aluePow t
  "sum" m̲atch k ? env ʰv̲alueSum t
  "group" m̲atch k ? "(" c̲at (env ʰv̲alueOf ᵖᵃi̲nner t) c̲at ")"
  "fn" m̲atch k ? env ʰv̲alueFn t
  "ref" m̲atch k ? env ʰv̲alueRef t
  env ʰv̲alueAtom ʰa̲tom t
}

ʰv̲alueChain : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> (Char, Char) -> Char

function (private) · line 421

A chain folded left: ((a + b) - c); a leading minus is 0 - a.

ʰv̲alueChain ← { env (ops, t) →
  (parts, os, neg) ← ("bad-equation right", ops) ᵖᵃp̲arts t
  first ← env ʰv̲alueOf d̲isclose f̲irst parts
  acc ← neg ʰp̲ickText ("(0 - " c̲at first c̲at ")", first)
  env ʰv̲alueFold (acc, os, 1 d̲rop parts)
}
Used in: ʰv̲alueOf

ʰv̲alueFold : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> (Char, Char, Box Char) -> Char

function (private) · line 429

The left fold of a chain: acc, then each operator with the next part.

ʰv̲alueFold ← { env (acc, os, parts) →
  0 = t̲ally os ? acc
  next ← "(" c̲at acc c̲at " " c̲at (1 t̲ake os) c̲at " " c̲at (env ʰv̲alueOf d̲isclose f̲irst parts) c̲at ")"
  env ʰv̲alueFold (next, 1 d̲rop os, 1 d̲rop parts)
}

ʰv̲aluePow : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> Char -> Char

function (private) · line 436

a ^ b, right to left: the text cut at its first top-level ^.

ʰv̲aluePow ← { env t →
  i ← f̲irst w̲here "^" ᵖᵃt̲op t
  "(" c̲at (env ʰv̲alueOf (i − 1) t̲ake t) c̲at " ^ " c̲at (env ʰv̲alueOf i d̲rop t) c̲at ")"
}
Used in: ʰv̲alueOf

ʰv̲alueSum : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> Char -> Char

function (private) · line 449

A parenthesized sum: a block with its own grid over the indices in scope and then its own, the body on that grid, and one reduction per binding (innermost first), leaving an array over the indices in scope. A scan is not a reduction (its term may use the one before, X_eTaL-ML ask E2) and is handled on its own, h:v_alueScan, before any of this -- it must be the term's only binding. Outside a scan, prev is refused wherever it is written: the word is reserved for the term a scan folds into the one before it.

ʰv̲alueSum ← { (names, spans, lows, cell, helpers) t →
  (ws, ns, ls, hs, os, body) ← ʰs̲umParts t
  isScan ← 0 < '+ r̲/ 0 + '{ w → (d̲isclose w) m̲atch "scan" } e̲ach ws
  _ ← ((n̲ot isScan) ∨ (1 = t̲ally ws)) ᵖᵃr̲equire ("bad-equation right", "a scan must be the only binding of its term (no sum, prod, max, min or integral alongside it): " c̲at t)
  isScan ? ʰv̲alueScan ((names, spans, lows, cell, helpers), d̲isclose f̲irst ns, d̲isclose f̲irst ls, d̲isclose f̲irst hs, d̲isclose f̲irst os, body)
  _ ← (n̲ot ʰh̲asPrev? body) ᵖᵃr̲equire ("bad-equation right", "prev names the term before inside a scan; it cannot be used here: " c̲at t)
  n ← t̲ally names
  _ ← ((t̲ally ns) = '+ r̲/ 0 + r̲avel (names c̲at ns) 'm̲atch t̲able ns) ᵖᵃr̲equire ("bad-equation right", "an index is bound once: " c̲at (", " ʰj̲oinWith (names c̲at ns)))
  qs ← '{ k → names ʰq̲uadrature (d̲isclose k s̲elect ws, d̲isclose k s̲elect ls, d̲isclose k s̲elect hs, d̲isclose k s̲elect os) } m̲ap r̲ange t̲ally ns
  q̲ ← { k j → d̲isclose j s̲elect d̲isclose k s̲elect qs }
  own ← '{ k → k q̲ 1 } m̲ap r̲ange t̲ally ns
  lows2 ← '{ k → k q̲ 2 } m̲ap r̲ange t̲ally ns
  grid ← ⎕G̲ENSYM "grid"
  cell2 ← ⎕G̲ENSYM "g_rid"
  env ← (names c̲at ns, spans c̲at own, lows c̲at lows2, cell2, helpers)
  reds ← "" ᵗj̲oin '{ k → (ʰr̲educer d̲isclose k s̲elect ws) c̲at " r_/_" c̲at (f̲ormat n + k) c̲at " " } m̲ap r̲ange t̲ally ns
  weighted ← "" ᵗj̲oin '{ k → ʰw̲eighted (k q̲ 4, cell2, n + k) } m̲ap r̲ange t̲ally ns
  none ← 0 = t̲ally spans c̲at own
  lines ← ("" ᵗj̲oin '{ k → k q̲ 3 } m̲ap r̲ange t̲ally ns) c̲at none ʰp̲ickText ("", ʰg̲ridLines (grid, cell2, spans c̲at own))
  shaped ← none ʰp̲ickText ("", grid c̲at " r_eshape ")
  "({ @ ->\n" c̲at lines c̲at "  " c̲at reds c̲at shaped c̲at weighted c̲at "(" c̲at (env ʰv̲alueOf body) c̲at ")\n} @)"
}
Used in: ʰv̲alueOf

ʰw̲ordLen : Char -> Int

function (private) · line 475

The length of the leading run of name characters in t (0 if t does not start with one): how h:r_enamed and h:h_asPrev? find a whole word instead of matching part of a longer name.

ʰw̲ordLen ← { t →
  0 = t̲ally t ? 0
  n̲ot (f̲irst t m̲ember? nameChars) ? 0
  1 + ʰw̲ordLen 1 d̲rop t
}

ʰr̲enamed : (Char, Char) -> Char -> Char

function (private) · line 486

t with every whole-word occurrence of from replaced by to, anything else untouched: how a scan's index and prev become plain generated names before the body is translated like any other expression (its own grammar has no notion of either, so they are never looked up through the grid env the other bindings use).

ʰr̲enamed ← { (from, to) t →
  0 = t̲ally t ? t
  n̲ot (f̲irst t m̲ember? nameChars) ? (1 t̲ake t) c̲at (from, to) ʰr̲enamed 1 d̲rop t
  k ← ʰw̲ordLen t
  word ← k t̲ake t
  piece ← ({ @ → (word m̲atch from) ? to◆ word } @)
  piece c̲at (from, to) ʰr̲enamed k d̲rop t
}

ʰh̲asPrev? : Truthy a => Char -> a

function (private) · line 497

Whether prev appears as a whole word anywhere in t: the word a scan's own body may use (its term before), refused everywhere else.

ʰh̲asPrev? ← { t →
  0 = t̲ally t ? 1 = 0
  n̲ot (f̲irst t m̲ember? nameChars) ? ʰh̲asPrev? 1 d̲rop t
  k ← ʰw̲ordLen t
  ((k t̲ake t) m̲atch "prev") ? 1 = 1
  ʰh̲asPrev? k d̲rop t
}

ʰv̲alueScan : ((Box Char, Box Char, Box Char, Char, (Char, Char, Char)), Char, Char, Char, Char, Char) -> Char

function (private) · line 519

A scan's value: a genuine fold, not a reduction -- the array machinery's reduce is a right fold (B6), only the same as a running left-to-right accumulation when the combining step is associative, which a scan's body need not be. Built instead from p_ower: one state-threading step function, (t, prev) to (t's successor, the body at that successor), applied once per point of the range, starting one step before the low end so the first real application lands there. prev starts at the seed (by, X_eTaL-ML ask E2); a scan with no seed and a body that never names prev folds in a seed of 0 harmlessly, since it is never read. The index and prev are substituted into the body's own translation (not its text: that would be reparsed as if it were the equation's grammar, which it is not) by name, so a scan's free indices, references and nested terms still translate through the ordinary env.

ʰv̲alueScan ← { (env, index, lo, hi, seed, body) →
  (names, _, _, _, _) ← env
  loCode ← names ʰb̲ound lo
  hiCode ← names ʰb̲ound hi
  _ ← ((0 < t̲ally seed) ∨ (n̲ot (ʰh̲asPrev? body))) ᵖᵃr̲equire ("bad-equation right", "a scan whose body uses prev needs a seed: scan " c̲at index c̲at ":=" c̲at lo c̲at ".." c̲at hi c̲at " by seed | " c̲at body)
  seedCode ← (0 = t̲ally seed) ʰp̲ickText ("0", names ʰb̲ound seed)
  rawBody ← env ʰv̲alueOf body
  (gStep, gT, gAcc, gNext) ← (⎕G̲ENSYM "step", ⎕G̲ENSYM "t", ⎕G̲ENSYM "acc", ⎕G̲ENSYM "t")
  stepBody ← (index, gNext) ʰr̲enamed (("prev", gAcc) ʰr̲enamed rawBody)
  "(({ @ ->\n  " c̲at gStep c̲at " := ({ @ -> ((" c̲at hiCode c̲at ") >= (" c̲at loCode c̲at ")) ? 1; 0 - 1 } @)\n  (_, " c̲at gAcc c̲at ") := (1 + a_bs ((" c̲at hiCode c̲at ") - (" c̲at loCode c̲at "))) '{ (" c̲at gT c̲at ", " c̲at gAcc c̲at ") ->\n    " c̲at gNext c̲at " := " c̲at gT c̲at " + " c̲at gStep c̲at "\n    (" c̲at gNext c̲at ", " c̲at stepBody c̲at ")\n  } p_ower ((" c̲at loCode c̲at ") - " c̲at gStep c̲at ", " c̲at seedCode c̲at ")\n  " c̲at gAcc c̲at "\n} @))"
}
Used in: ʰv̲alueSum

ʰq̲uadrature : Box Char -> (Char, Char, Char, Char) -> Box Char

function (private) · line 542

A binding's span, coordinate prefix, defining lines and weights name, as texts: a sum's span is its range's length, checked at run time (low and high are the caller's values, not known until then): a negative length -- the range runs high..low, backwards -- signals error[bad-equation] naming the word and the range as written, rather than falling through to the array machinery's own count error (X_eTaL-ML ask E1); its coordinate is its low plus the cell, no lines, no weights; an integral's span is its grid's n + 1 points, its coordinate a + h * the cell, its lines define a, b, h and the weights (fresh names, so the body may name any of a, b, h or w), the weights holding the rule's and the step.

ʰq̲uadrature ← { names (w, lo, hi, opt) →
  n̲ot "integral" m̲atch w ? (e̲nclose "({ v -> (0 <= v) ? v; \"bad-equation\" []S_IGNAL \"" c̲at w c̲at "'s range runs low..high: " c̲at lo c̲at ".." c̲at hi c̲at "\" } (1 + " c̲at (names ʰb̲ound hi) c̲at " - " c̲at (names ʰb̲ound lo) c̲at "))") c̲at (e̲nclose "(" c̲at (names ʰb̲ound lo) c̲at " + ") c̲at (e̲nclose "") c̲at e̲nclose ""
  (rule, k) ← ʰr̲uleSteps opt
  (a, b, h, ws) ← (⎕G̲ENSYM "a", ⎕G̲ENSYM "b", ⎕G̲ENSYM "h", ⎕G̲ENSYM "w")
  lines ← "  " c̲at a c̲at " := f_loat (" c̲at (names ʰb̲ound lo) c̲at ")\n  " c̲at b c̲at " := f_loat (" c̲at (names ʰb̲ound hi) c̲at ")\n  " c̲at h c̲at " := (" c̲at b c̲at " - " c̲at a c̲at ") / " c̲at (f̲ormat k) c̲at ".0\n  " c̲at ws c̲at " := " c̲at h c̲at " * " c̲at (rule ʰw̲eights k) c̲at "\n"
  (e̲nclose "(" c̲at (f̲ormat k + 1) c̲at ")") c̲at (e̲nclose "(" c̲at a c̲at " + " c̲at h c̲at " * f_loat ") c̲at (e̲nclose lines) c̲at e̲nclose ws
}
Used in: ʰv̲alueSum

ʰw̲eights : Char -> Int -> Char

function (private) · line 552

The rule's weights over k steps (k + 1 points), as code: trapezoid 1/2, 1, ..., 1, 1/2; simpson (1, 4, 2, 4, ..., 4, 1) / 3.

ʰw̲eights ← { rule k →
  rule m̲atch "simpson" ? "(1.0 c_at (" c̲at (f̲ormat k − 1) c̲at " r_eshape 4.0 2.0) c_at 1.0) / 3.0"
  "0.5 c_at (" c̲at (f̲ormat k − 1) c̲at " r_eshape 1.0) c_at 0.5"
}

ʰw̲eighted : (Char, Char, Int) -> Char

function (private) · line 559

The factor an integral's binding puts before the body: its weights laid over the grid along its axis; "" for a sum.

ʰw̲eighted ← { (ws, cell, axis) →
  0 = t̲ally ws ? ""
  "((1 + " c̲at cell c̲at " " c̲at (f̲ormat axis) c̲at ") s_elect " c̲at ws c̲at ") * "
}
Used in: ʰv̲alueSum

ʰr̲educer : Char -> Char

function (private) · line 565

The reduction an operator word stands for.

ʰr̲educer ← { w →
  "sum" m̲atch w ? "'+"
  "prod" m̲atch w ? "'*"
  "max" m̲atch w ? "'m_ax"
  "integral" m̲atch w ? "'+"
  "'m_in"
}
Used in: ʰv̲alueSum

ʰb̲ound : Box Char -> Char -> Char

function (private) · line 575

A bound or extent: an expression of the caller's values, with no index in scope; one that names an index is refused.

ʰb̲ound ← { names t →
  used ← '{ w → 0 < '+ r̲/ 0 + '{ n → (d̲isclose n) m̲atch d̲isclose w } e̲ach names } e̲ach ᵗw̲ords ʰs̲paced t
  _ ← (0 = '+ r̲/ 0 + used) ᵖᵃr̲equire ("bad-equation right", "a bound or extent cannot depend on an index: " c̲at t)
  (e̲nclose "", e̲nclose "", e̲nclose "", "", ("", "", "")) ʰv̲alueOf t
}

ʰs̲paced : Char -> Char

function (private) · line 582

t with its operators and brackets spaced out, so its words are its names.

ʰs̲paced ← { t → "" ᵗj̲oin '{ ch → (ch m̲ember? "+-*/^()[],") ? " "◆ ch } m̲ap t }
Used in: ʰb̲ound

ʰv̲alueFn : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> Char -> Char

function (private) · line 590

A function applied to its arguments' values: the fixed math functions by their own rule, any other name as the caller's own u: function, applied to however many arguments it was given (X_eTaL-ML ask E3) -- u:name(args), not a quoted call, so a name that is not a function, or not one of this arity, is the expanded code's own compile error, naming the equation's call.

ʰv̲alueFn ← { env t →
  (name, args) ← ʰf̲nParts t
  vs ← '{ a → env ʰv̲alueOf d̲isclose a } m̲ap args
  a ← d̲isclose f̲irst vs
  "sqrt" m̲atch name ? "(" c̲at a c̲at " ^ 0.5)"
  "exp" m̲atch name ? "(e_xp " c̲at a c̲at ")"
  "log" m̲atch name ? "(l_og " c̲at a c̲at ")"
  "abs" m̲atch name ? "(a_bs " c̲at a c̲at ")"
  "sin" m̲atch name ? "(s_in " c̲at a c̲at ")"
  "cos" m̲atch name ? "(c_os " c̲at a c̲at ")"
  (("max" m̲atch name) ∨ ("min" m̲atch name)) ? "(" c̲at a c̲at (("max" m̲atch name) ʰp̲ickText (" m_ax ", " m_in ")) c̲at (d̲isclose 2 s̲elect vs) c̲at ")"
  "(u:" c̲at name c̲at "(" c̲at (", " ᵗj̲oin vs) c̲at "))"
}
Used in: ʰv̲alueOf

ʰv̲alueRef : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> Char -> Char

function (private) · line 607

A[e1,...,en] as a gather from A's ravel, the index by Horner's rule over A's shape (0-origin, as the equation writes it); A is checked to have n axes, and each index to lie within its axis.

ʰv̲alueRef ← { (names, spans, lows, cell, (at, chk, rk)) t →
  (name, ix) ← "bad-equation right" ᵖᵃr̲eference t
  inner ← (names, spans, lows, cell, (at, chk, rk)) ʰh̲orner (name, ix, t̲ally ix)
  checked ← "(" c̲at rk c̲at " (" c̲at name c̲at ", \"" c̲at name c̲at "\", " c̲at (f̲ormat t̲ally ix) c̲at ", \"" c̲at t c̲at "\"))"
  "\n    (" c̲at checked c̲at " " c̲at at c̲at " (" c̲at inner c̲at "))"
}
Used in: ʰv̲alueOf

ʰh̲orner : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> (Char, Box Char, Int) -> Char

function (private) · line 617

The ravel position of A[e1,...,ek] by Horner's rule: ek + (k-th extent of A) * (the same for e1 ... e(k-1)), down to e1; each ek passes through the bounds check.

ʰh̲orner ← { (names, spans, lows, cell, (at, chk, rk)) (name, ix, k) →
  env ← (names, spans, lows, cell, (at, chk, rk))
  written ← ᵗt̲rim d̲isclose k s̲elect ix
  e ← "\n      ((" c̲at name c̲at ", \"" c̲at name c̲at "\", " c̲at (f̲ormat k) c̲at ", \"" c̲at written c̲at "\") " c̲at chk c̲at " " c̲at (env ʰv̲alueOf written) c̲at ")"
  k = 1 ? e
  "(" c̲at e c̲at " + (" c̲at (f̲ormat k) c̲at " s_elect s_hape " c̲at name c̲at ") * " c̲at (env ʰh̲orner (name, ix, k − 1)) c̲at ")"
}

ʰv̲alueAtom : (Box Char, Box Char, Box Char, Char, (Char, Char, Char)) -> Char -> Char

function (private) · line 627

An index in scope as its coordinates on the grid (its low added), anything else as written: the caller's name or a number.

ʰv̲alueAtom ← { (names, _, lows, cell, _) t →
  hit ← w̲here '{ n → (d̲isclose n) m̲atch t } e̲ach names
  0 = t̲ally hit ? t
  k ← f̲irst hit
  (d̲isclose k s̲elect lows) c̲at cell c̲at " " c̲at (f̲ormat k) c̲at ")"
}
Used in: ʰv̲alueOf

ʰc̲hecks : (Char, Char) -> Char

function (private) · line 638

The two run-time checks the expansion defines: an index within its axis (0 to the extent less one), and a reference with as many indices as its array has axes; each names the array, the axis and the expression as written, and gives its argument back when it holds.

ʰc̲hecks ← { (chk, rk) →
  c ← "  " c̲at chk c̲at " := { (A, name, k, from) e ->\n    n := k s_elect s_hape A\n    bad := w_here n_ot r_avel (0 <= e) & e < n\n    0 = t_ally bad ? e\n    \"index\" []S_IGNAL name c_at \" axis \" c_at (f_ormat k) c_at \": index \" c_at (f_ormat (f_irst bad) s_elect r_avel e) c_at \" is outside 0..\" c_at (f_ormat n - 1) c_at \" (from \" c_at from c_at \")\"\n  }\n"
  r ← "  " c̲at rk c̲at " := { (A, name, q, ref) ->\n    (t_ally s_hape A) = q ? A\n    \"rank\" []S_IGNAL ref c_at \" indexes \" c_at (f_ormat q) c_at \" axes, but \" c_at name c_at \" has rank \" c_at f_ormat t_ally s_hape A\n  }\n"
  c c̲at r
}
Used in: ʰv̲alue

The same call as LaTeX

ʰt̲ex : Num a => a -> Char -> Char -> Char

function (private) · line 652

The LaTeX text of the call (no quotes): the result as written (L for a scalar), = and the expression; a bare sum without the parentheses the reader gave it. on is 1 to mark the symbols (e:t_exSymbols<), 0 for plain LaTeX; every helper below takes the marking mk = (on, the index names in scope), the free indices here, and each sum adds its own for its bounds and body.

ʰt̲ex ← { on lhs rhs →
  (names, _) ← ʰf̲ree lhs
  head ← ᵗt̲rim d̲isclose f̲irst "|" ᵖᵃs̲plit lhs
  name ← ((f̲irst w̲here head m̲ember? "[") − 1) t̲ake head
  sub ← (0 = t̲ally names) ʰp̲ickText ("", "_{" c̲at ("," ᵗj̲oin names) c̲at "}")
  mk ← (on, names)
  ((mk ʰm̲ark name)_ (ʰg̲reek name) c̲at sub) c̲at " = " c̲at mk ʰt̲exBody ᵗt̲rim rhs
}

ʰm̲ark : Num a => (a, Box Char) -> Char -> Char -> Char

function (private) · line 663

The LaTeX tex of the symbol name, wrapped in its classes when the marking mk is on.

ʰm̲ark ← { mk name tex →
  (on, _) ← mk
  0 = on ? tex
  "\\\\htmlClass{sym sym-" c̲at name c̲at "}{" c̲at tex c̲at "}"
}

ʰt̲exBody : Num a => (a, Box Char) -> Char -> Char

function (private) · line 671

The right side as LaTeX: a bare sum without the parentheses the reader gives it, anything else as a term.

ʰt̲exBody ← { mk t →
  0 < '+ r̲/ 0 + "|" ᵖᵃt̲op t ? ((mk ʰt̲exSum ("", ""))_ t)
  mk ʰt̲exOf ʰw̲hole t
}
Used in: ʰt̲ex

ʰt̲exOf : Num a => (a, Box Char) -> Char -> Char

function (private) · line 677

A term as LaTeX.

ʰt̲exOf ← { mk text →
  t ← ᵗt̲rim text
  k ← ʰk̲ind t
  "add" m̲atch k ? ((mk ʰt̲exChain "+-")_ t)
  "mul" m̲atch k ? ((mk ʰt̲exChain "*/")_ t)
  "pow" m̲atch k ? mk ʰt̲exPow t
  "sum" m̲atch k ? ((mk ʰt̲exSum ("\\\\left(", "\\\\right)"))_ ᵖᵃi̲nner t)
  "group" m̲atch k ? "\\\\left(" c̲at (mk ʰt̲exOf ᵖᵃi̲nner t) c̲at "\\\\right)"
  "fn" m̲atch k ? mk ʰt̲exFn t
  "ref" m̲atch k ? mk ʰt̲exRef t
  mk ʰt̲exAtom t
}

ʰt̲exAtom : Num a => (a, Box Char) -> Char -> Char

function (private) · line 692

A name or a number: a Greek letter's name as its command; a named scalar marked (not a number, an index in scope or prev).

ʰt̲exAtom ← { mk text →
  (_, ix) ← mk
  t ← ʰa̲tom text
  number ← (f̲irst t) m̲ember? "0123456789."
  bound ← 0 < '+ r̲/ 0 + '{ w → (d̲isclose w) m̲atch t } e̲ach ix
  number ∨ bound ∨ "prev" m̲atch t ? ʰg̲reek t
  ((mk ʰm̲ark t)_ ʰg̲reek t)
}
Used in: ʰt̲exOf

ʰt̲exChain : Num a => (a, Box Char) -> Char -> Char -> Char

function (private) · line 702

A chain: a + b - c; a \, b for a product; \frac{a}{b} for a quotient.

ʰt̲exChain ← { mk ops t →
  (parts, os, neg) ← ("bad-equation right", ops) ᵖᵃp̲arts t
  p1 ← d̲isclose f̲irst parts
  first ← ("/" m̲atch 1 t̲ake os c̲at " ") ʰp̲ickText (mk ʰt̲exBare p1, mk ʰt̲exOf p1)
  acc ← neg ʰp̲ickText ("-" c̲at first, first)
  mk ʰt̲exFold (acc, os, 1 d̲rop parts)
}
Used in: ʰt̲exOf

ʰt̲exFold : Num a => (a, Box Char) -> (Char, Char, Box Char) -> Char

function (private) · line 710
ʰt̲exFold ← { mk (acc, os, parts) →
  0 = t̲ally os ? acc
  op ← 1 t̲ake os
  p ← d̲isclose f̲irst parts
  next ← ("/" m̲atch op) ʰp̲ickText (mk ʰt̲exBare p, mk ʰt̲exOf p)
  mk ʰt̲exFold (ʰt̲exJoin (acc, op, next), 1 d̲rop os, 1 d̲rop parts)
}

ʰt̲exBare : Num a => (a, Box Char) -> Char -> Char

function (private) · line 721

A term without the parentheses it was written in, where what holds it already sets it apart: the numerator or denominator of \frac, the argument of \sqrt.

ʰt̲exBare ← { mk text →
  t ← ᵗt̲rim text
  k ← ʰk̲ind t
  "sum" m̲atch k ? ((mk ʰt̲exSum ("", ""))_ ᵖᵃi̲nner t)
  "group" m̲atch k ? mk ʰt̲exOf ᵖᵃi̲nner t
  mk ʰt̲exOf t
}

ʰt̲exJoin : (Char, Char, Char) -> Char

function (private) · line 730

Two operands joined by an operator: \frac for /, a thin space for *.

ʰt̲exJoin ← { (acc, op, next) →
  "/" m̲atch op ? "\\\\frac{" c̲at acc c̲at "}{" c̲at next c̲at "}"
  "*" m̲atch op ? acc c̲at " \\\\, " c̲at next
  acc c̲at " " c̲at op c̲at " " c̲at next
}
Used in: ʰt̲exFold

ʰt̲exPow : Num a => (a, Box Char) -> Char -> Char

function (private) · line 736
ʰt̲exPow ← { mk t →
  i ← f̲irst w̲here "^" ᵖᵃt̲op t
  (mk ʰt̲exOf (i − 1) t̲ake t) c̲at "^{" c̲at (mk ʰt̲exOf i d̲rop t) c̲at "}"
}
Used in: ʰt̲exOf

ʰt̲exSum : Num a => (a, Box Char) -> (Char, Char) -> Char -> Char

function (private) · line 744

A sum's bindings as \sum_{i=low}^{high} (the operator word's symbol), then its body, wrapped as asked; its indices are in scope for its bounds and its body.

ʰt̲exSum ← { mk (open, close) inner →
  (ws, ns, ls, hs, _, body) ← ʰs̲umParts "(" c̲at inner c̲at ")"
  (on, ix) ← mk
  m ← (on, ix c̲at ns)
  o̲ne ← { k → (ʰs̲ymbol d̲isclose k s̲elect ws) c̲at "_{" c̲at (ʰt̲exIndex (d̲isclose k s̲elect ws, d̲isclose k s̲elect ns)) c̲at (m ʰt̲exOf d̲isclose k s̲elect ls) c̲at "}^{" c̲at (m ʰt̲exOf d̲isclose k s̲elect hs) c̲at "} " }
  d̲ ← { k → ("integral" m̲atch d̲isclose k s̲elect ws) ʰp̲ickText (" \\\\, d" c̲at (ʰg̲reek d̲isclose k s̲elect ns), "") }
  open c̲at ("" ᵗj̲oin 'o̲ne m̲ap r̲ange t̲ally ns) c̲at (m ʰt̲exOf body) c̲at ("" ᵗj̲oin 'd̲ m̲ap r̲ev r̲ange t̲ally ns) c̲at close
}

ʰt̲exIndex : (Char, Char) -> Char

function (private) · line 755

A sum's index and = before its low; an integral's lower bound stands alone (\int_{a}^{b}).

ʰt̲exIndex ← { (w, i) →
  "integral" m̲atch w ? ""
  i c̲at "="
}
Used in: ʰt̲exSum

ʰg̲reek : Char -> Char

function (private) · line 762

A Greek letter's name as its LaTeX command (a capital's too: Phi, Sigma); any other name as is.

ʰg̲reek ← { v →
  '∨ r̲/ 0 + '{ w → (d̲isclose w) m̲atch v } e̲ach ᵗw̲ords greek ? "\\\\" c̲at v
  v
}

greek : Char

value (private) · line 766
greek ← "alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi pi rho sigma tau upsilon phi chi psi omega Gamma Delta Theta Lambda Xi Pi Sigma Upsilon Phi Psi Omega"
Used in: ʰg̲reek

ʰs̲ymbol : Char -> Char

function (private) · line 769

The symbol of an operator word.

ʰs̲ymbol ← { w →
  "sum" m̲atch w ? "\\\\sum"
  "prod" m̲atch w ? "\\\\prod"
  "max" m̲atch w ? "\\\\max"
  "integral" m̲atch w ? "\\\\int"
  "scan" m̲atch w ? "\\\\operatorname{scan}"
  "\\\\min"
}
Used in: ʰt̲exSum

ʰt̲exFn : Num a => (a, Box Char) -> Char -> Char

function (private) · line 779

A function applied: \exp(x), \log(x), \sqrt{x}, \lvert x \rvert, \max(a, b).

ʰt̲exFn ← { mk t →
  (name, args) ← ʰf̲nParts t
  as ← '{ a → mk ʰt̲exOf d̲isclose a } m̲ap args
  a ← d̲isclose f̲irst as
  known ← 0 < '+ r̲/ 0 + '{ f → (d̲isclose f) m̲atch name } e̲ach functions
  "sqrt" m̲atch name ? "\\\\sqrt{" c̲at (mk ʰt̲exBare d̲isclose f̲irst args) c̲at "}"
  "abs" m̲atch name ? "\\\\lvert " c̲at a c̲at " \\\\rvert"
  known ? "\\\\" c̲at name c̲at "(" c̲at (", " ᵗj̲oin as) c̲at ")"
  "\\\\operatorname{" c̲at name c̲at "}(" c̲at (", " ᵗj̲oin as) c̲at ")"
}
Used in: ʰt̲exOf

ʰt̲exRef : Num a => (a, Box Char) -> Char -> Char

function (private) · line 792

A reference as a subscript: A_{i,j}, the indices as written without spaces, a product among them as a thin space; marked as a whole.

ʰt̲exRef ← { mk t →
  (name, ix) ← "bad-equation right" ᵖᵃr̲eference t
  ((mk ʰm̲ark name)_ name c̲at "_{" c̲at ("," ᵗj̲oin '{ i → ʰs̲ubscript d̲isclose i } m̲ap ix) c̲at "}")
}
Used in: ʰt̲exOf

ʰs̲ubscript : Char -> Char

function (private) · line 797
ʰs̲ubscript ← { t → "" ᵗj̲oin '{ ch → (ch m̲ember? "*") ? "\\\\,"◆ (ch m̲ember? " ") ? ""◆ ch } m̲ap t }
Used in: ʰt̲exRef

The reading written back

ʰp̲lain : Char -> Char

function (private) · line 803

A term as it was read, with no spaces: the same text it came from, or a rule dropped something.

ʰp̲lain ← { text →
  t ← ᵗt̲rim text
  k ← ʰk̲ind t
  "add" m̲atch k ? "+-" ʰp̲lainChain t
  "mul" m̲atch k ? "*/" ʰp̲lainChain t
  "pow" m̲atch k ? ʰp̲lainPow t
  "sum" m̲atch k ? ʰp̲lainSum t
  "group" m̲atch k ? "(" c̲at (ʰp̲lain ᵖᵃi̲nner t) c̲at ")"
  "fn" m̲atch k ? ʰp̲lainFn t
  "ref" m̲atch k ? ʰp̲lainRef t
  ʰa̲tom t
}

ʰp̲lainChain : Char -> Char -> Char

function (private) · line 816
ʰp̲lainChain ← { ops t →
  (parts, os, neg) ← ("bad-equation right", ops) ᵖᵃp̲arts t
  first ← ʰp̲lain d̲isclose f̲irst parts
  acc ← neg ʰp̲ickText ("-" c̲at first, first)
  ʰp̲lainFold (acc, os, 1 d̲rop parts)
}
Used in: ʰp̲lain

ʰp̲lainFold : (Char, Char, Box Char) -> Char

function (private) · line 823
ʰp̲lainFold ← { (acc, os, parts) →
  0 = t̲ally os ? acc
  ʰp̲lainFold (acc c̲at (1 t̲ake os) c̲at (ʰp̲lain d̲isclose f̲irst parts), 1 d̲rop os, 1 d̲rop parts)
}

ʰp̲lainPow : Char -> Char

function (private) · line 828
ʰp̲lainPow ← { t →
  i ← f̲irst w̲here "^" ᵖᵃt̲op t
  (ʰp̲lain (i − 1) t̲ake t) c̲at "^" c̲at ʰp̲lain i d̲rop t
}
Used in: ʰp̲lain

ʰp̲lainSum : Char -> Char

function (private) · line 833
ʰp̲lainSum ← { t →
  (ws, ns, ls, hs, os, body) ← ʰs̲umParts t
  o̲ne ← { k → (d̲isclose k s̲elect ws) c̲at (d̲isclose k s̲elect ns) c̲at ":=" c̲at (ʰp̲lain d̲isclose k s̲elect ls) c̲at ".." c̲at (ʰp̲lain d̲isclose k s̲elect hs) c̲at ʰp̲lainBy d̲isclose k s̲elect os }
  "(" c̲at ("," ᵗj̲oin 'o̲ne m̲ap r̲ange t̲ally ns) c̲at "|" c̲at (ʰp̲lain body) c̲at ")"
}
Used in: ʰp̲lain

ʰp̲lainBy : Char -> Char

function (private) · line 840

An integral's by options as written, squeezed; "" for none.

ʰp̲lainBy ← { opt →
  0 = t̲ally opt ? ""
  "by" c̲at ᵖᵃs̲queeze opt
}
Used in: ʰp̲lainSum

ʰp̲lainFn : Char -> Char

function (private) · line 845
ʰp̲lainFn ← { t →
  (name, args) ← ʰf̲nParts t
  name c̲at "(" c̲at ("," ᵗj̲oin '{ a → ʰp̲lain d̲isclose a } m̲ap args) c̲at ")"
}
Used in: ʰp̲lain

ʰp̲lainRef : Char -> Char

function (private) · line 850
ʰp̲lainRef ← { t →
  (name, ix) ← "bad-equation right" ᵖᵃr̲eference t
  name c̲at "[" c̲at ("," ᵗj̲oin '{ i → ʰp̲lain d̲isclose i } m̲ap ix) c̲at "]"
}
Used in: ʰp̲lain

Text helpers

ʰj̲oinWith : Char -> Box Char -> Char

function (private) · line 858

The boxed texts b joined by sep.

ʰj̲oinWith ← { sep b → sep ᵗj̲oin b }