summaryrefslogtreecommitdiff
path: root/tests/suite/math
diff options
context:
space:
mode:
Diffstat (limited to 'tests/suite/math')
-rw-r--r--tests/suite/math/accent.typ33
-rw-r--r--tests/suite/math/alignment.typ34
-rw-r--r--tests/suite/math/attach.typ130
-rw-r--r--tests/suite/math/cancel.typ38
-rw-r--r--tests/suite/math/cases.typ13
-rw-r--r--tests/suite/math/class.typ47
-rw-r--r--tests/suite/math/delimited.typ64
-rw-r--r--tests/suite/math/equation.typ212
-rw-r--r--tests/suite/math/frac.typ43
-rw-r--r--tests/suite/math/interactions.typ95
-rw-r--r--tests/suite/math/mat.typ163
-rw-r--r--tests/suite/math/multiline.typ109
-rw-r--r--tests/suite/math/op.typ30
-rw-r--r--tests/suite/math/primes.typ50
-rw-r--r--tests/suite/math/root.typ45
-rw-r--r--tests/suite/math/size.typ9
-rw-r--r--tests/suite/math/spacing.typ59
-rw-r--r--tests/suite/math/style.typ34
-rw-r--r--tests/suite/math/syntax.typ34
-rw-r--r--tests/suite/math/text.typ45
-rw-r--r--tests/suite/math/underover.typ21
-rw-r--r--tests/suite/math/vec.typ27
22 files changed, 1335 insertions, 0 deletions
diff --git a/tests/suite/math/accent.typ b/tests/suite/math/accent.typ
new file mode 100644
index 00000000..9f57d69b
--- /dev/null
+++ b/tests/suite/math/accent.typ
@@ -0,0 +1,33 @@
+// Test math accents.
+
+--- math-accent-sym-call ---
+// Test function call.
+$grave(a), acute(b), hat(f), tilde(§), macron(ä), diaer(a), ä \
+ breve(\&), dot(!), circle(a), caron(@), arrow(Z), arrow.l(Z)$
+
+--- math-accent-align ---
+$ x &= p \ dot(x) &= v \ dot.double(x) &= a \ dot.triple(x) &= j \ dot.quad(x) &= s $
+
+--- math-accent-func ---
+// Test `accent` function.
+$accent(ö, .), accent(v, <-), accent(ZZ, \u{0303})$
+
+--- math-accent-bounds ---
+// Test accent bounds.
+$sqrt(tilde(T)) + hat(f)/hat(g)$
+
+--- math-accent-wide-base ---
+// Test wide base.
+$arrow("ABC" + d), tilde(sum)$
+
+--- math-accent-superscript ---
+// Test effect of accent on superscript.
+$A^x != hat(A)^x != hat(hat(A))^x$
+
+--- math-accent-high-base ---
+// Test high base.
+$ tilde(integral), tilde(integral)_a^b, tilde(integral_a^b) $
+
+--- math-accent-sized ---
+// Test accent size.
+$tilde(sum), tilde(sum, size: #50%), accent(H, hat, size: #200%)$
diff --git a/tests/suite/math/alignment.typ b/tests/suite/math/alignment.typ
new file mode 100644
index 00000000..f110b139
--- /dev/null
+++ b/tests/suite/math/alignment.typ
@@ -0,0 +1,34 @@
+// Test implicit alignment math.
+
+--- math-align-weird ---
+// Test alignment step functions.
+#set page(width: 225pt)
+$
+"a" &= c \
+&= c + 1 & "By definition" \
+&= d + 100 + 1000 \
+&= x && "Even longer" \
+$
+
+--- math-align-post-fix ---
+// Test post-fix alignment.
+$
+& "right" \
+"a very long line" \
+"left" \
+$
+
+--- math-align-implicit ---
+// Test no alignment.
+$
+"right" \
+"a very long line" \
+"left" \
+$
+
+--- math-align-toggle ---
+// Test #460 equations.
+$
+a &=b & quad c&=d \
+e &=f & g&=h
+$
diff --git a/tests/suite/math/attach.typ b/tests/suite/math/attach.typ
new file mode 100644
index 00000000..c9510c6a
--- /dev/null
+++ b/tests/suite/math/attach.typ
@@ -0,0 +1,130 @@
+// Test t and b attachments, part 1.
+
+--- math-attach-postscripts ---
+// Test basics, postscripts.
+$f_x + t^b + V_1^2 + attach(A, t: alpha, b: beta)$
+
+--- math-attach-prescripts ---
+// Test basics, prescripts. Notably, the upper and lower prescripts' content need to be
+// aligned on the right edge of their bounding boxes, not on the left as in postscripts.
+$
+attach(upright(O), bl: 8, tl: 16, br: 2, tr: 2-),
+attach("Pb", bl: 82, tl: 207) + attach(upright(e), bl: -1, tl: 0) + macron(v)_e \
+$
+
+--- math-attach-mixed ---
+// A mixture of attachment positioning schemes.
+$
+attach(a, tl: u), attach(a, tr: v), attach(a, bl: x),
+attach(a, br: y), limits(a)^t, limits(a)_b \
+
+attach(a, tr: v, t: t),
+attach(a, tr: v, br: y),
+attach(a, br: y, b: b),
+attach(limits(a), b: b, bl: x),
+attach(a, tl: u, bl: x),
+attach(limits(a), t: t, tl: u) \
+
+attach(a, tl: u, tr: v),
+attach(limits(a), t: t, br: y),
+attach(limits(a), b: b, tr: v),
+attach(a, bl: x, br: y),
+attach(limits(a), b: b, tl: u),
+attach(limits(a), t: t, bl: u),
+limits(a)^t_b \
+
+attach(a, tl: u, tr: v, bl: x, br: y),
+attach(limits(a), t: t, bl: x, br: y, b: b),
+attach(limits(a), t: t, tl: u, tr: v, b: b),
+attach(limits(a), tl: u, bl: x, t: t, b: b),
+attach(limits(a), t: t, b: b, tr: v, br: y),
+attach(a, tl: u, t: t, tr: v, bl: x, b: b, br: y)
+$
+
+--- math-attach-followed-by-func-call ---
+// Test function call after subscript.
+$pi_1(Y), a_f(x), a^zeta (x), a^abs(b)_sqrt(c) \
+ a^subset.eq (x), a_(zeta(x)), pi_(1(Y)), a^(abs(b))_(sqrt(c))$
+
+--- math-attach-nested ---
+// Test associativity and scaling.
+$ 1/(V^2^3^4^5),
+ frac(
+ attach(
+ limits(V), br: attach(2, br: 3), b: attach(limits(2), b: 3)),
+ attach(
+ limits(V), tl: attach(2, tl: 3), t: attach(limits(2), t: 3))),
+ attach(Omega,
+ tl: attach(2, tl: attach(3, tl: attach(4, tl: 5))),
+ tr: attach(2, tr: attach(3, tr: attach(4, tr: 5))),
+ bl: attach(2, bl: attach(3, bl: attach(4, bl: 5))),
+ br: attach(2, br: attach(3, br: attach(4, br: 5))),
+ )
+$
+
+--- math-attach-high ---
+// Test high subscript and superscript.
+$ sqrt(a_(1/2)^zeta), sqrt(a_alpha^(1/2)), sqrt(a_(1/2)^(3/4)) \
+ sqrt(attach(a, tl: 1/2, bl: 3/4)),
+ sqrt(attach(a, tl: 1/2, bl: 3/4, tr: 1/2, br: 3/4)) $
+
+--- math-attach-descender-collision ---
+// Test for no collisions between descenders/ascenders and attachments
+
+$ sup_(x in P_i) quad inf_(x in P_i) $
+$ op("fff",limits: #true)^(y) quad op("yyy", limits:#true)_(f) $
+
+--- math-attach-to-group ---
+// Test frame base.
+$ (-1)^n + (1/2 + 3)^(-1/2) $
+
+--- math-attach-horizontal-align ---
+#set text(size: 8pt)
+
+// Test that the attachments are aligned horizontally.
+$ x_1 p_1 frak(p)_1 2_1 dot_1 lg_1 !_1 \\_1 ]_1 "ip"_1 op("iq")_1 \
+ x^1 b^1 frak(b)^1 2^1 dot^1 lg^1 !^1 \\^1 ]^1 "ib"^1 op("id")^1 \
+ x_1 y_1 "_"_1 x^1 l^1 "`"^1 attach(I,tl:1,bl:1,tr:1,br:1)
+ scripts(sum)_1^1 integral_1^1 abs(1/2)_1^1 \
+ x^1_1, "("b y")"^1_1 != (b y)^1_1, "[∫]"_1 [integral]_1 $
+
+--- math-attach-limit ---
+// Test limit.
+$ lim_(n->oo \ n "grows") sum_(k=0 \ k in NN)^n k $
+
+--- math-attach-force-scripts-and-limits ---
+// Test forcing scripts and limits.
+$ limits(A)_1^2 != A_1^2 $
+$ scripts(sum)_1^2 != sum_1^2 $
+$ limits(integral)_a^b != integral_a^b $
+
+--- issue-math-attach-realize-panic ---
+// Error: 25-29 unknown variable: oops
+$ attach(A, t: #context oops) $
+
+--- math-attach-show-limit ---
+// Show and let rules for limits and scripts
+#let eq = $ ∫_a^b iota_a^b $
+#eq
+#show "∫": math.limits
+#show math.iota: math.limits.with(inline: false)
+#eq
+$iota_a^b$
+
+--- math-attach-default-placement ---
+// Test default of limit attachments on relations at all sizes
+#set page(width: auto)
+$ a =^"def" b quad a lt.eq_"really" b quad a arrow.r.long.squiggly^"slowly" b $
+$a =^"def" b quad a lt.eq_"really" b quad a arrow.r.long.squiggly^"slowly" b$
+
+$a scripts(=)^"def" b quad a scripts(lt.eq)_"really" b quad a scripts(arrow.r.long.squiggly)^"slowly" b$
+
+--- math-attach-integral ---
+// Test default of scripts attachments on integrals at display size
+$ integral.sect_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b $
+$integral.sect_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b$
+
+--- math-attach-large-operator ---
+// Test default of limit attachments on large operators at display size only
+$ tack.t.big_0^1 quad \u{02A0A}_0^1 quad join_0^1 $
+$tack.t.big_0^1 quad \u{02A0A}_0^1 quad join_0^1$
diff --git a/tests/suite/math/cancel.typ b/tests/suite/math/cancel.typ
new file mode 100644
index 00000000..e2fd5efd
--- /dev/null
+++ b/tests/suite/math/cancel.typ
@@ -0,0 +1,38 @@
+// Tests the cancel() function.
+
+--- math-cancel-inline ---
+// Inline
+$a + 5 + cancel(x) + b - cancel(x)$
+
+$c + (a dot.c cancel(b dot.c c))/(cancel(b dot.c c))$
+
+--- math-cancel-display ---
+// Display
+#set page(width: auto)
+$ a + b + cancel(b + c) - cancel(b) - cancel(c) - 5 + cancel(6) - cancel(6) $
+$ e + (a dot.c cancel((b + c + d)))/(cancel(b + c + d)) $
+
+--- math-cancel-inverted ---
+// Inverted
+$a + cancel(x, inverted: #true) - cancel(x, inverted: #true) + 10 + cancel(y) - cancel(y)$
+$ x + cancel("abcdefg", inverted: #true) $
+
+--- math-cancel-cross ---
+// Cross
+$a + cancel(b + c + d, cross: #true, stroke: #red) + e$
+$ a + cancel(b + c + d, cross: #true) + e $
+
+--- math-cancel-customized ---
+// Resized and styled
+#set page(width: 200pt, height: auto)
+$a + cancel(x, length: #200%) - cancel(x, length: #50%, stroke: #(red + 1.1pt))$
+$ b + cancel(x, length: #150%) - cancel(a + b + c, length: #50%, stroke: #(blue + 1.2pt)) $
+
+--- math-cancel-angle-absolute ---
+// Specifying cancel line angle with an absolute angle
+$cancel(x, angle: #0deg) + cancel(x, angle: #45deg) + cancel(x, angle: #90deg) + cancel(x, angle: #135deg)$
+
+--- math-cancel-angle-func ---
+// Specifying cancel line angle with a function
+$x + cancel(y, angle: #{angle => angle + 90deg}) - cancel(z, angle: #(angle => angle + 135deg))$
+$ e + cancel((j + e)/(f + e)) - cancel((j + e)/(f + e), angle: #(angle => angle + 30deg)) $
diff --git a/tests/suite/math/cases.typ b/tests/suite/math/cases.typ
new file mode 100644
index 00000000..e6c4956d
--- /dev/null
+++ b/tests/suite/math/cases.typ
@@ -0,0 +1,13 @@
+// Test case distinction.
+
+--- math-cases ---
+$ f(x, y) := cases(
+ 1 quad &"if" (x dot y)/2 <= 0,
+ 2 &"if" x divides 2,
+ 3 &"if" x in NN,
+ 4 &"else",
+) $
+
+--- math-cases-gap ---
+#set math.cases(gap: 1em)
+$ x = cases(1, 2) $
diff --git a/tests/suite/math/class.typ b/tests/suite/math/class.typ
new file mode 100644
index 00000000..7aad0446
--- /dev/null
+++ b/tests/suite/math/class.typ
@@ -0,0 +1,47 @@
+// Test math classes.
+
+--- math-class-chars ---
+// Test characters.
+$ a class("normal", +) b \
+ a class("binary", .) b \
+ lr(class("opening", \/) a/b class("closing", \\)) \
+ { x class("fence", \;) x > 0} \
+ a class("large", \/) b \
+ a class("punctuation", :) b \
+ a class("relation", ~) b \
+ a + class("unary", times) b \
+ class("vary", :) a class("vary", :) b $
+
+--- math-class-content ---
+// Test custom content.
+#let dotsq = square(
+ size: 0.7em,
+ stroke: 0.5pt,
+ align(center+horizon, circle(radius: 0.15em, fill: black))
+)
+
+$ a dotsq b \
+ a class("normal", dotsq) b \
+ a class("vary", dotsq) b \
+ a + class("vary", dotsq) b \
+ a class("punctuation", dotsq) b $
+
+--- math-class-nested ---
+// Test nested.
+#let normal = math.class.with("normal")
+#let pluseq = $class("binary", normal(+) normal(=))$
+$ a pluseq 5 $
+
+--- math-class-exceptions ---
+// Test exceptions.
+$ sqrt(3)\/2 quad d_0.d_1d_2 dots $
+
+--- math-class-limits ---
+// Test if the math class changes the limit configuration.
+$ class("normal", ->)_a $
+$class("relation", x)_a$
+$ class("large", x)_a $
+$class("large", ->)_a$
+
+$limits(class("normal", ->))_a$
+$ scripts(class("relation", x))_a $
diff --git a/tests/suite/math/delimited.typ b/tests/suite/math/delimited.typ
new file mode 100644
index 00000000..98579947
--- /dev/null
+++ b/tests/suite/math/delimited.typ
@@ -0,0 +1,64 @@
+// Test delimiter matching and scaling.
+
+--- math-lr-matching ---
+// Test automatic matching.
+#set page(width:122pt)
+$ (a) + {b/2} + abs(a)/2 + (b) $
+$f(x/2) < zeta(c^2 + abs(a + b/2))$
+
+--- math-lr-unmatched ---
+// Test unmatched.
+$[1,2[ = [1,2) != zeta\(x/2\) $
+
+--- math-lr-call ---
+// Test manual matching.
+$ [|a/b|] != lr(|]a/b|]) != [a/b) $
+$ lr(| ]1,2\[ + 1/2|) $
+
+--- math-lr-fences ---
+// Test fence confusion.
+$ |x + |y| + z/a| \
+ lr(|x + |y| + z/a|) $
+
+--- math-lr-symbol-unmatched ---
+// Test that symbols aren't matched automatically.
+$ bracket.l a/b bracket.r
+ = lr(bracket.l a/b bracket.r) $
+
+--- math-lr-half ---
+// Test half LRs.
+$ lr(a/b\]) = a = lr(\{a/b) $
+
+--- math-lr-size ---
+// Test manual scaling.
+$ lr(]sum_(x=1)^n x], size: #70%)
+ < lr((1, 2), size: #200%) $
+
+--- math-lr-shorthands ---
+// Test predefined delimiter pairings.
+$floor(x/2), ceil(x/2), abs(x), norm(x)$
+
+--- math-lr-color ---
+// Test colored delimiters
+$ lr(
+ text("(", fill: #green) a/b
+ text(")", fill: #blue)
+ ) $
+
+--- math-lr-mid ---
+// Test middle functions
+$ { x mid(|) sum_(i=1)^oo phi_i (x) < 1 } \
+ { integral |x| dif x
+ mid(bar.v.double)
+ floor(hat(A) mid(|) { x mid(|) y } mid(|) A) } $
+
+--- math-lr-unbalanced ---
+// Test unbalanced delimiters.
+$ 1/(2 (x) $
+$ 1_(2 y (x) () $
+$ 1/(2 y (x) (2(3)) $
+
+--- math-lr-weak-spacing ---
+// Test ignoring weak spacing immediately after the opening
+// and immediately before the closing.
+$ [#h(1em, weak: true)A(dif x, f(x) dif x)sum#h(1em, weak: true)] $
diff --git a/tests/suite/math/equation.typ b/tests/suite/math/equation.typ
new file mode 100644
index 00000000..dd2745d1
--- /dev/null
+++ b/tests/suite/math/equation.typ
@@ -0,0 +1,212 @@
+// Test alignment of block equations.
+// Test show rules on equations.
+
+--- math-equation-numbering ---
+#set page(width: 150pt)
+#set math.equation(numbering: "(I)")
+
+We define $x$ in preparation of @fib:
+$ phi.alt := (1 + sqrt(5)) / 2 $ <ratio>
+
+With @ratio, we get
+$ F_n = round(1 / sqrt(5) phi.alt^n) $ <fib>
+
+--- math-equation-font ---
+// Test different font.
+#show math.equation: set text(font: "Fira Math")
+$ v := vec(1 + 2, 2 - 4, sqrt(3), arrow(x)) + 1 $
+
+--- math-equation-show-rule ---
+This is small: $sum_(i=0)^n$
+
+#show math.equation: math.display
+This is big: $sum_(i=0)^n$
+
+--- math-equation-align-unnumbered ---
+// Test unnumbered
+#let eq(alignment) = {
+ show math.equation: set align(alignment)
+ $ a + b = c $
+}
+
+#eq(center)
+#eq(left)
+#eq(right)
+
+#set text(dir: rtl)
+#eq(start)
+#eq(end)
+
+--- math-equation-align-numbered ---
+// Test numbered
+#let eq(alignment) = {
+ show math.equation: set align(alignment)
+ $ a + b = c $
+}
+
+#set math.equation(numbering: "(1)")
+
+#eq(center)
+#eq(left)
+#eq(right)
+
+#set text(dir: rtl)
+#eq(start)
+#eq(end)
+
+--- math-equation-number-align ---
+#set math.equation(numbering: "(1)")
+
+$ a + b = c $
+
+#show math.equation: set align(center)
+$ a + b = c $
+#show math.equation: set align(left)
+$ a + b = c $
+#show math.equation: set align(right)
+$ a + b = c $
+
+#set text(dir: rtl)
+#show math.equation: set align(start)
+$ a + b = c $
+#show math.equation: set align(end)
+$ a + b = c $
+
+--- math-equation-number-align-start ---
+#set math.equation(numbering: "(1)", number-align: start)
+
+$ a + b = c $
+
+#show math.equation: set align(center)
+$ a + b = c $
+#show math.equation: set align(left)
+$ a + b = c $
+#show math.equation: set align(right)
+$ a + b = c $
+
+#set text(dir: rtl)
+#show math.equation: set align(start)
+$ a + b = c $
+#show math.equation: set align(end)
+$ a + b = c $
+
+--- math-equation-number-align-end ---
+#set math.equation(numbering: "(1)", number-align: end)
+
+$ a + b = c $
+
+#show math.equation: set align(center)
+$ a + b = c $
+#show math.equation: set align(left)
+$ a + b = c $
+#show math.equation: set align(right)
+$ a + b = c $
+
+#set text(dir: rtl)
+#show math.equation: set align(start)
+$ a + b = c $
+#show math.equation: set align(end)
+$ a + b = c $
+
+--- math-equation-number-align-left ---
+#set math.equation(numbering: "(1)", number-align: left)
+
+$ a + b = c $
+
+#show math.equation: set align(center)
+$ a + b = c $
+#show math.equation: set align(left)
+$ a + b = c $
+#show math.equation: set align(right)
+$ a + b = c $
+
+#set text(dir: rtl)
+#show math.equation: set align(start)
+$ a + b = c $
+#show math.equation: set align(end)
+$ a + b = c $
+
+--- math-equation-number-align-right ---
+#set math.equation(numbering: "(1)", number-align: right)
+
+$ a + b = c $
+
+#show math.equation: set align(center)
+$ a + b = c $
+#show math.equation: set align(left)
+$ a + b = c $
+#show math.equation: set align(right)
+$ a + b = c $
+
+#set text(dir: rtl)
+#show math.equation: set align(start)
+$ a + b = c $
+#show math.equation: set align(end)
+$ a + b = c $
+
+--- math-equation-number-align-center ---
+// Error: 52-58 expected `start`, `left`, `right`, or `end`, found center
+#set math.equation(numbering: "(1)", number-align: center)
+
+--- math-equation-number-align-center-bottom ---
+// Error: 52-67 expected `start`, `left`, `right`, or `end`, found center
+#set math.equation(numbering: "(1)", number-align: center + bottom)
+
+--- math-equation-number-align-multiline ---
+#set math.equation(numbering: "(1)")
+
+$ p &= ln a b \
+ &= ln a + ln b $
+
+--- math-equation-number-align-multiline-top-start ---
+#set math.equation(numbering: "(1)", number-align: top+start)
+
+$ p &= ln a b \
+ &= ln a + ln b $
+
+--- math-equation-number-align-multiline-bottom ---
+#show math.equation: set align(left)
+#set math.equation(numbering: "(1)", number-align: bottom)
+
+$ q &= ln sqrt(a b) \
+ &= 1/2 (ln a + ln b) $
+
+--- math-equation-number-align-multiline-expand ---
+// Tests that if the numbering's layout box vertically exceeds the box of
+// the equation frame's boundary, the latter's frame is resized correctly
+// to encompass the numbering. #box() below delineates the resized frame.
+//
+// A row with "-" only has a height that's smaller than the height of the
+// numbering's layout box. Note we use pattern "1" here, not "(1)", since
+// the parenthesis exceeds the numbering's layout box, due to the default
+// settings of top-edge and bottom-edge of the TextElem that laid it out.
+#set math.equation(numbering: "1", number-align: top)
+#box(
+$ - &- - \
+ a &= b $,
+fill: silver)
+
+#set math.equation(numbering: "1", number-align: horizon)
+#box(
+$ - - - $,
+fill: silver)
+
+#set math.equation(numbering: "1", number-align: bottom)
+#box(
+$ a &= b \
+ - &- - $,
+fill: silver)
+
+--- issue-numbering-hint ---
+// In this bug, the hint and error messages for an equation
+// being reference mentioned that it was a "heading" and was
+// lacking the proper path.
+#set page(height: 70pt)
+
+$
+ Delta = b^2 - 4 a c
+$ <quadratic>
+
+// Error: 14-24 cannot reference equation without numbering
+// Hint: 14-24 you can enable equation numbering with `#set math.equation(numbering: "1.")`
+Looks at the @quadratic formula.
diff --git a/tests/suite/math/frac.typ b/tests/suite/math/frac.typ
new file mode 100644
index 00000000..b3ca8aa0
--- /dev/null
+++ b/tests/suite/math/frac.typ
@@ -0,0 +1,43 @@
+// Test fractions.
+
+--- math-frac-baseline ---
+// Test that denominator baseline matches in the common case.
+$ x = 1/2 = a/(a h) = a/a = a/(1/2) $
+
+--- math-frac-paren-removal ---
+// Test parenthesis removal.
+$ (|x| + |y|)/2 < [1+2]/3 $
+
+--- math-frac-large ---
+// Test large fraction.
+$ x = (-b plus.minus sqrt(b^2 - 4a c))/(2a) $
+
+--- math-binom ---
+// Test binomial.
+$ binom(circle, square) $
+
+--- math-binom-multiple ---
+// Test multinomial coefficients.
+$ binom(n, k_1, k_2, k_3) $
+
+--- math-binom-missing-lower ---
+// Error: 3-13 missing argument: lower
+$ binom(x^2) $
+
+--- math-dif ---
+// Test dif.
+$ (dif y)/(dif x), dif/x, x/dif, dif/dif \
+ frac(dif y, dif x), frac(dif, x), frac(x, dif), frac(dif, dif) $
+
+--- math-frac-associativity ---
+// Test associativity.
+$ 1/2/3 = (1/2)/3 = 1/(2/3) $
+
+--- math-frac-precedence ---
+// Test precedence.
+$ a_1/b_2, 1/f(x), zeta(x)/2, "foo"[|x|]/2 \
+ 1.2/3.7, 2.3^3.4 \
+ 🏳️‍🌈[x]/2, f [x]/2, phi [x]/2, 🏳️‍🌈 [x]/2 \
+ +[x]/2, 1(x)/2, 2[x]/2 \
+ (a)b/2, b(a)[b]/2 \
+ n!/2, 5!/2, n !/2, 1/n!, 1/5! $
diff --git a/tests/suite/math/interactions.typ b/tests/suite/math/interactions.typ
new file mode 100644
index 00000000..37185962
--- /dev/null
+++ b/tests/suite/math/interactions.typ
@@ -0,0 +1,95 @@
+// Test interactions with styling and normal layout.
+// Hint: They are bad ...
+
+--- math-nested-normal-layout ---
+// Test images and font fallback.
+#let monkey = move(dy: 0.2em, image("/assets/images/monkey.svg", height: 1em))
+$ sum_(i=#emoji.apple)^#emoji.apple.red i + monkey/2 $
+
+--- math-table ---
+// Test tables.
+$ x := #table(columns: 2)[x][y]/mat(1, 2, 3)
+ = #table[A][B][C] $
+
+--- math-equation-auto-wrapping ---
+// Test non-equation math directly in content.
+#math.attach($a$, t: [b])
+
+--- math-font-switch ---
+// Test font switch.
+#let here = text.with(font: "Noto Sans")
+$#here[f] := #here[Hi there]$.
+
+--- math-box-without-baseline ---
+// Test boxes without a baseline act as if the baseline is at the base
+#{
+ box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $a$)$)
+ h(12pt)
+ box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $g$)$)
+ h(12pt)
+ box(stroke: 0.2pt, $g #box(stroke: 0.2pt, $g$)$)
+}
+
+--- math-box-with-baseline ---
+// Test boxes with a baseline are respected
+#box(stroke: 0.2pt, $a #box(baseline:0.5em, stroke: 0.2pt, $a$)$)
+
+--- issue-2821-missing-fields ---
+// Issue #2821: Setting a figure's supplement to none removes the field
+#show figure.caption: it => {
+ assert(it.has("supplement"))
+ assert(it.supplement == none)
+}
+#figure([], caption: [], supplement: none)
+
+--- math-symbol-show-rule ---
+// Test using rules for symbols
+#show sym.tack: it => $#h(1em) it #h(1em)$
+$ a tack b $
+
+--- issue-math-realize-show ---
+// Test that content in math can be realized without breaking
+// nested equations.
+#let my = $pi$
+#let f1 = box(baseline: 10pt, [f])
+#let f2 = context f1
+#show math.vec: [nope]
+
+$ pi a $
+$ my a $
+$ 1 + sqrt(x/2) + sqrt(#hide($x/2$)) $
+$ a x #link("url", $+ b$) $
+$ f f1 f2 $
+$ vec(1,2) * 2 $
+
+--- issue-math-realize-hide ---
+$ x^2 #hide[$(>= phi.alt) union y^2 0$] z^2 $
+Hello #hide[there $x$]
+and #hide[$ f(x) := x^2 $]
+
+--- issue-math-realize-scripting ---
+// Test equations can embed equation pieces built by functions
+#let foo(v1, v2) = {
+ // Return an equation piece that would've been rendered in
+ // inline style if the piece is not embedded
+ $v1 v2^2$
+}
+#let bar(v1, v2) = {
+ // Return an equation piece that would've been rendered in
+ // block style if the piece is not embedded
+ $ v1 v2^2 $
+}
+#let baz(..sink) = {
+ // Return an equation piece built by joining arrays
+ sink.pos().map(x => $hat(#x)$).join(sym.and)
+}
+
+Inline $2 foo(alpha, (M+foo(a, b)))$.
+
+Inline $2 bar(alpha, (M+foo(a, b)))$.
+
+Inline $2 baz(x,y,baz(u, v))$.
+
+$ 2 foo(alpha, (M+foo(a, b))) $
+$ 2 bar(alpha, (M+foo(a, b))) $
+$ 2 baz(x,y,baz(u, v)) $
diff --git a/tests/suite/math/mat.typ b/tests/suite/math/mat.typ
new file mode 100644
index 00000000..e6148a34
--- /dev/null
+++ b/tests/suite/math/mat.typ
@@ -0,0 +1,163 @@
+// Test matrices.
+
+--- math-mat-semicolon ---
+// Test semicolon syntax.
+#set align(center)
+$mat() dot
+ mat(;) dot
+ mat(1, 2) dot
+ mat(1, 2;) \
+ mat(1; 2) dot
+ mat(1, 2; 3, 4) dot
+ mat(1 + &2, 1/2; &3, 4)$
+
+--- math-mat-sparse ---
+// Test sparse matrix.
+$ mat(
+ 1, 2, ..., 10;
+ 2, 2, ..., 10;
+ dots.v, dots.v, dots.down, dots.v;
+ 10, 10, ..., 10;
+) $
+
+--- math-mat-baseline ---
+// Test baseline alignment.
+$ mat(
+ a, b^2;
+ sum_(x \ y) x, a^(1/2);
+ zeta, alpha;
+) $
+
+--- math-mat-delim-set ---
+// Test alternative delimiter with set rule.
+#set math.mat(delim: "[")
+$ mat(1, 2; 3, 4) $
+$ a + mat(delim: #none, 1, 2; 3, 4) + b $
+
+--- math-mat-delim-direct ---
+// Test alternative math delimiter directly in call.
+#set align(center)
+#grid(
+ columns: 3,
+ gutter: 10pt,
+
+ $ mat(1, 2, delim: "[") $,
+ $ mat(1, 2; delim: "[") $,
+ $ mat(delim: "[", 1, 2) $,
+
+ $ mat(1; 2; delim: "[") $,
+ $ mat(1; delim: "[", 2) $,
+ $ mat(delim: "[", 1; 2) $,
+
+ $ mat(1, 2; delim: "[", 3, 4) $,
+ $ mat(delim: "[", 1, 2; 3, 4) $,
+ $ mat(1, 2; 3, 4; delim: "[") $,
+)
+
+--- math-mat-gap ---
+#set math.mat(gap: 1em)
+$ mat(1, 2; 3, 4) $
+
+--- math-mat-gaps ---
+#set math.mat(row-gap: 1em, column-gap: 2em)
+$ mat(1, 2; 3, 4) $
+
+--- math-mat-augment ---
+// Test matrix line drawing (augmentation).
+#grid(
+ columns: 2,
+ gutter: 10pt,
+
+ $ mat(10, 2, 3, 4; 5, 6, 7, 8; augment: #3) $,
+ $ mat(10, 2, 3, 4; 5, 6, 7, 8; augment: #(-1)) $,
+ $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: 2)) $,
+ $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: -1)) $,
+ $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: 1, vline: 1)) $,
+ $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(hline: -2, vline: -2)) $,
+ $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(vline: 2, stroke: 1pt + blue)) $,
+ $ mat(100, 2, 3; 4, 5, 6; 7, 8, 9; augment: #(vline: -1, stroke: 1pt + blue)) $,
+)
+
+--- math-mat-augment-set ---
+// Test using matrix line drawing with a set rule.
+#set math.mat(augment: (hline: 2, vline: 1, stroke: 2pt + green))
+$ mat(1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 1) $
+
+#set math.mat(augment: 2)
+$ mat(1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 1) $
+
+#set math.mat(augment: none)
+
+--- math-mat-augment-line-out-of-bounds ---
+// Error: 3-37 cannot draw a vertical line after column 3 of a matrix with 3 columns
+$ mat(1, 0, 0; 0, 1, 1; augment: #3) $,
+
+--- math-mat-align-explicit--alternating ---
+// Test alternating explicit alignment in a matrix.
+$ mat(
+ "a" & "a a a" & "a a";
+ "a a" & "a a" & "a";
+ "a a a" & "a" & "a a a";
+) $
+
+--- math-mat-align-implicit ---
+// Test alignment in a matrix.
+$ mat(
+ "a", "a a a", "a a";
+ "a a", "a a", "a";
+ "a a a", "a", "a a a";
+) $
+
+--- math-mat-align-explicit-left ---
+// Test explicit left alignment in a matrix.
+$ mat(
+ &"a", &"a a a", &"a a";
+ &"a a", &"a a", &"a";
+ &"a a a", &"a", &"a a a";
+) $
+
+--- math-mat-align-explicit-right ---
+// Test explicit right alignment in a matrix.
+$ mat(
+ "a"&, "a a a"&, "a a"&;
+ "a a"&, "a a"&, "a"&;
+ "a a a"&, "a"&, "a a a"&;
+) $
+
+--- math-mat-align-complex ---
+// Test #460 equations.
+#let stop = {
+ math.class("punctuation",$.$)
+}
+$ mat(&a+b,c;&d, e) $
+$ mat(&a+b&,c;&d&, e) $
+$ mat(&&&a+b,c;&&&d, e) $
+$ mat(stop &a+b&stop,c;...stop stop&d&...stop stop, e) $
+
+--- math-mat-align-signed-numbers ---
+// Test #454 equations.
+$ mat(-1, 1, 1; 1, -1, 1; 1, 1, -1) $
+$ mat(-1&, 1&, 1&; 1&, -1&, 1&; 1&, 1&, -1&) $
+$ mat(-1&, 1&, 1&; 1, -1, 1; 1, 1, -1) $
+$ mat(&-1, &1, &1; 1, -1, 1; 1, 1, -1) $
+
+--- math-mat-bad-comma ---
+// This error message is bad.
+// Error: 13-14 expected array, found content
+$ mat(1, 2; 3, 4, delim: "[") $,
+
+--- issue-852-mat-type ---
+$ mat(B, A B) $
+$ mat(B, A B, dots) $
+$ mat(B, A B, dots;) $
+$ mat(#1, #(foo: "bar")) $
+
+--- issue-2268-mat-augment-color ---
+// The augment line should be of the same color as the text
+#set text(
+ font: "New Computer Modern",
+ lang: "en",
+ fill: yellow,
+)
+
+$mat(augment: #1, M, v) arrow.r.squiggly mat(augment: #1, R, b)$
diff --git a/tests/suite/math/multiline.typ b/tests/suite/math/multiline.typ
new file mode 100644
index 00000000..85433627
--- /dev/null
+++ b/tests/suite/math/multiline.typ
@@ -0,0 +1,109 @@
+// Test multiline math.
+
+--- math-align-basic ---
+// Test basic alignment.
+$ x &= x + y \
+ &= x + 2z \
+ &= sum x dot 2z $
+
+--- math-align-wider-first-column ---
+// Test text before first alignment point.
+$ x + 1 &= a^2 + b^2 \
+ y &= a + b^2 \
+ z &= alpha dot beta $
+
+--- math-align-aligned-in-source ---
+// Test space between inner alignment points.
+$ a + b &= 2 + 3 &= 5 \
+ b &= c &= 3 $
+
+--- math-align-cases ---
+// Test in case distinction.
+$ f := cases(
+ 1 + 2 &"iff" &x,
+ 3 &"if" &y,
+) $
+
+--- math-align-lines-mixed ---
+// Test mixing lines with and some without alignment points.
+$ "abc" &= c \
+ &= d + 1 \
+ = x $
+
+--- math-attach-subscript-multiline ---
+// Test multiline subscript.
+$ sum_(n in NN \ n <= 5) n = (5(5+1))/2 = 15 $
+
+--- math-multiline-no-trailing-linebreak ---
+// Test no trailing line break.
+$
+"abc" &= c
+$
+No trailing line break.
+
+--- math-multiline-trailing-linebreak ---
+// Test single trailing line break.
+$
+"abc" &= c \
+$
+One trailing line break.
+
+--- math-multiline-multiple-trailing-linebreaks ---
+// Test multiple trailing line breaks.
+$
+"abc" &= c \ \ \
+$
+Multiple trailing line breaks.
+
+--- math-linebreaking-after-binop-and-rel ---
+// Basic breaking after binop, rel
+#let hrule(x) = box(line(length: x))
+#hrule(45pt)$e^(pi i)+1 = 0$\
+#hrule(55pt)$e^(pi i)+1 = 0$\
+#hrule(70pt)$e^(pi i)+1 = 0$
+
+--- math-linebreaking-lr ---
+// LR groups prevent linbreaking.
+#let hrule(x) = box(line(length: x))
+#hrule(76pt)$a+b$\
+#hrule(74pt)$(a+b)$\
+#hrule(74pt)$paren.l a+b paren.r$
+
+--- math-linebreaking-multiline ---
+// Multiline yet inline does not linebreak
+#let hrule(x) = box(line(length: x))
+#hrule(80pt)$a + b \ c + d$\
+
+--- math-linebreaking-trailing-linebreak ---
+// A single linebreak at the end still counts as one line.
+#let hrule(x) = box(line(length: x))
+#hrule(60pt)$e^(pi i)+1 = 0\ $
+
+--- math-linebreaking-in-box ---
+// Inline, in a box, doesn't linebreak.
+#let hrule(x) = box(line(length: x))
+#hrule(80pt)#box($a+b$)
+
+--- math-linebreaking-between-consecutive-relations ---
+// A relation followed by a relation doesn't linebreak
+#let hrule(x) = box(line(length: x))
+#hrule(70pt)$a < = b$\
+#hrule(74pt)$a < = b$
+
+--- math-linebreaking-after-relation-without-space ---
+// Line breaks can happen after a relation even if there is no
+// explicit space.
+#let hrule(x) = box(line(length: x))
+#hrule(90pt)$<;$\
+#hrule(95pt)$<;$\
+#hrule(90pt)$<)$\
+#hrule(95pt)$<)$
+
+--- math-linebreaking-empty ---
+// Verify empty rows are handled ok.
+$ $\
+Nothing: $ $, just empty.
+
+--- issue-1948-math-text-break ---
+// Test text with linebreaks in math.
+$ x := "a\nb\nc\nd\ne" $
diff --git a/tests/suite/math/op.typ b/tests/suite/math/op.typ
new file mode 100644
index 00000000..4139a08b
--- /dev/null
+++ b/tests/suite/math/op.typ
@@ -0,0 +1,30 @@
+// Test text operators.
+
+--- math-op-predefined ---
+// Test predefined.
+$ max_(1<=n<=m) n $
+
+--- math-op-call ---
+// With or without parens.
+$ &sin x + log_2 x \
+ = &sin(x) + log_2(x) $
+
+--- math-op-scripts-vs-limits ---
+// Test scripts vs limits.
+#set page(width: auto)
+#set text(font: "New Computer Modern")
+Discuss $lim_(n->oo) 1/n$ now.
+$ lim_(n->infinity) 1/n = 0 $
+
+--- math-op-custom ---
+// Test custom operator.
+$ op("myop", limits: #false)_(x:=1) x \
+ op("myop", limits: #true)_(x:=1) x $
+
+--- math-op-styled ---
+// Test styled operator.
+$ bold(op("bold", limits: #true))_x y $
+
+--- math-non-math-content ---
+// With non-text content
+$ op(#underline[ul]) a $
diff --git a/tests/suite/math/primes.typ b/tests/suite/math/primes.typ
new file mode 100644
index 00000000..e10f8876
--- /dev/null
+++ b/tests/suite/math/primes.typ
@@ -0,0 +1,50 @@
+--- math-primes ---
+// Test dedicated syntax for primes
+$a'$, $a'''_b$, $'$, $'''''''$
+
+--- math-primes-spaces ---
+// Test spaces between
+$a' ' '$, $' ' '$, $a' '/b$
+
+--- math-primes-complex ---
+// Test complex prime combinations
+$a'_b^c$, $a_b'^c$, $a_b^c'$, $a_b'^c'^d'$
+
+$(a'_b')^(c'_d')$, $a'/b'$, $a_b'/c_d'$
+
+$∫'$, $∑'$, $ ∑'_S' $
+
+--- math-primes-attach ---
+// Test attaching primes only
+$a' = a^', a_', a_'''^''^'$
+
+--- math-primes-scripts ---
+// Test primes always attaching as scripts
+$ x' $
+$ x^' $
+$ attach(x, t: ') $
+$ <' $
+$ attach(<, br: ') $
+$ op(<, limits: #true)' $
+$ limits(<)' $
+
+--- math-primes-limits ---
+// Test forcefully attaching primes as limits
+$ attach(<, t: ') $
+$ <^' $
+$ attach(<, b: ') $
+$ <_' $
+
+$ limits(x)^' $
+$ attach(limits(x), t: ') $
+
+--- math-primes-after-code-expr ---
+// Test prime symbols after code mode.
+#let g = $f$
+#let gg = $f$
+
+$
+ #(g)' #g' #g ' \
+ #g''''''''''''''''' \
+ gg'
+$
diff --git a/tests/suite/math/root.typ b/tests/suite/math/root.typ
new file mode 100644
index 00000000..a690802e
--- /dev/null
+++ b/tests/suite/math/root.typ
@@ -0,0 +1,45 @@
+// Test roots.
+
+--- math-root-basic ---
+// Test root with more than one character.
+$A = sqrt(x + y) = c$
+
+--- math-root-radical-attachment ---
+// Test root size with radicals containing attachments.
+$ sqrt(a) quad
+ sqrt(f) quad
+ sqrt(q) quad
+ sqrt(a^2) \
+ sqrt(n_0) quad
+ sqrt(b^()) quad
+ sqrt(b^2) quad
+ sqrt(q_1^2) $
+
+--- math-root-precomposed ---
+// Test precomposed vs constructed roots.
+// 3 and 4 are precomposed.
+$sqrt(x)$
+$root(2, x)$
+$root(3, x)$
+$root(4, x)$
+$root(5, x)$
+
+--- math-root-large-body ---
+// Test large bodies
+$ sqrt([|x|]^2 + [|y|]^2) < [|z|] $
+$ v = sqrt((1/2) / (4/5))
+ = root(3, (1/2/3) / (4/5/6))
+ = root(4, ((1/2) / (3/4)) / ((1/2) / (3/4))) $
+
+--- math-root-large-index ---
+// Test large index.
+$ root(2, x) quad
+ root(3/(2/1), x) quad
+ root(1/11, x) quad
+ root(1/2/3, 1) $
+
+--- math-root-syntax ---
+// Test shorthand.
+$ √2^3 = sqrt(2^3) $
+$ √(x+y) quad ∛x quad ∜x $
+$ (√2+3) = (sqrt(2)+3) $
diff --git a/tests/suite/math/size.typ b/tests/suite/math/size.typ
new file mode 100644
index 00000000..d0d41dc9
--- /dev/null
+++ b/tests/suite/math/size.typ
@@ -0,0 +1,9 @@
+--- math-size ---
+// Test forcing math size
+$a/b, display(a/b), display(a)/display(b), inline(a/b), script(a/b), sscript(a/b) \
+ mono(script(a/b)), script(mono(a/b))\
+ script(a^b, cramped: #true), script(a^b, cramped: #false)$
+
+--- issue-3658-math-size ---
+$ #rect[$1/2$] $
+$#rect[$1/2$]$
diff --git a/tests/suite/math/spacing.typ b/tests/suite/math/spacing.typ
new file mode 100644
index 00000000..2a387f92
--- /dev/null
+++ b/tests/suite/math/spacing.typ
@@ -0,0 +1,59 @@
+// Test spacing in math formulas.
+
+--- math-spacing-basic ---
+// Test spacing cases.
+$ä, +, c, (, )$ \
+$=), (+), {times}$ \
+$⟧<⟦, abs(-), [=$ \
+$a=b, a==b$ \
+$-a, +a$ \
+$a not b$ \
+$a+b, a*b$ \
+$sum x, sum(x)$ \
+$sum product x$ \
+$f(x), zeta(x), "frac"(x)$ \
+$a+dots.c+b$
+$f(x) sin(y)$
+
+--- math-spacing-kept-spaces ---
+// Test ignored vs non-ignored spaces.
+$f (x), f(x)$ \
+$[a|b], [a | b]$ \
+$a"is"b, a "is" b$
+
+--- math-spacing-predefined ---
+// Test predefined spacings.
+$a thin b, a med b, a thick b, a quad b$ \
+$a = thin b$ \
+$a - b equiv c quad (mod 2)$
+
+--- math-spacing-set-comprehension ---
+// Test spacing for set comprehension.
+#set page(width: auto)
+$ { x in RR | x "is natural" and x < 10 } $
+
+--- math-spacing-decorated ---
+// Test spacing for operators with decorations and modifiers on them
+#set page(width: auto)
+$a equiv b + c - d => e log 5 op("ln") 6$ \
+$a cancel(equiv) b overline(+) c arrow(-) d hat(=>) e cancel(log) 5 dot(op("ln")) 6$ \
+$a overbrace(equiv) b underline(+) c grave(-) d underbracket(=>) e circle(log) 5 caron(op("ln")) 6$ \
+\
+$a attach(equiv, tl: a, tr: b) b attach(limits(+), t: a, b: b) c tilde(-) d breve(=>) e attach(limits(log), t: a, b: b) 5 attach(op("ln"), tr: a, bl: b) 6$
+
+--- math-spacing-weak ---
+// Test weak spacing
+$integral f(x) dif x$,
+// Not weak
+$integral f(x) thin dif x$,
+// Both are weak, collide
+$integral f(x) #h(0.166em, weak: true)dif x$
+
+--- issue-1052-math-number-spacing ---
+// Test spacing after numbers in math.
+$
+10degree \
+10 degree \
+10.1degree \
+10.1 degree
+$
diff --git a/tests/suite/math/style.typ b/tests/suite/math/style.typ
new file mode 100644
index 00000000..09ddd3c1
--- /dev/null
+++ b/tests/suite/math/style.typ
@@ -0,0 +1,34 @@
+// Test text styling in math.
+
+--- math-style-italic-default ---
+// Test italic defaults.
+$a, A, delta, ϵ, diff, Delta, ϴ$
+
+--- math-style ---
+// Test forcing a specific style.
+$A, italic(A), upright(A), bold(A), bold(upright(A)), \
+ serif(A), sans(A), cal(A), frak(A), mono(A), bb(A), \
+ italic(diff), upright(diff), \
+ bb("hello") + bold(cal("world")), \
+ mono("SQRT")(x) wreath mono(123 + 456)$
+
+--- math-style-exceptions ---
+// Test a few style exceptions.
+$h, bb(N), cal(R), Theta, italic(Theta), sans(Theta), sans(italic(Theta)) \
+ bb(d), bb(italic(d)), italic(bb(d)), bb(e), bb(italic(e)), italic(bb(e)) \
+ bb(i), bb(italic(i)), italic(bb(i)), bb(j), bb(italic(j)), italic(bb(j)) \
+ bb(D), bb(italic(D)), italic(bb(D))$
+
+--- math-style-greek-exceptions ---
+// Test a few greek exceptions.
+$bb(Gamma) , bb(gamma), bb(Pi), bb(pi), bb(sum)$
+
+--- math-style-hebrew-exceptions ---
+// Test hebrew exceptions.
+$aleph, beth, gimel, daleth$
+
+--- issue-3650-italic-equation ---
+_abc $sin(x) "abc"$_ \
+$italic(sin(x) "abc" #box[abc])$ \
+*abc $sin(x) "abc"$* \
+$bold(sin(x) "abc" #box[abc])$ \
diff --git a/tests/suite/math/syntax.typ b/tests/suite/math/syntax.typ
new file mode 100644
index 00000000..fcb8b89e
--- /dev/null
+++ b/tests/suite/math/syntax.typ
@@ -0,0 +1,34 @@
+// Test math syntax.
+
+--- math-call-non-func ---
+$ pi(a) $
+$ pi(a,) $
+$ pi(a,b) $
+$ pi(a,b,) $
+
+--- math-unicode ---
+// Test Unicode math.
+$ ∑_(i=0)^ℕ a ∘ b = \u{2211}_(i=0)^NN a compose b $
+
+--- math-shorthandes ---
+// Test a few shorthands.
+$ underline(f' : NN -> RR) \
+ n |-> cases(
+ [|1|] &"if" n >>> 10,
+ 2 * 3 &"if" n != 5,
+ 1 - 0 thick &...,
+ ) $
+
+--- math-common-symbols ---
+// Test common symbols.
+$ dot \ dots \ ast \ tilde \ star $
+
+--- issue-2044-invalid-parsed-ident ---
+// In this bug, the dot at the end was causing the right parenthesis to be
+// parsed as an identifier instead of the closing right parenthesis.
+$floor(phi.alt.)$
+$floor(phi.alt. )$
+
+--- math-unclosed ---
+// Error: 1-2 unclosed delimiter
+$a
diff --git a/tests/suite/math/text.typ b/tests/suite/math/text.typ
new file mode 100644
index 00000000..760910f4
--- /dev/null
+++ b/tests/suite/math/text.typ
@@ -0,0 +1,45 @@
+// Test that setting font features in math.equation has an effect.
+
+--- math-font-fallback ---
+// Test font fallback.
+$ よ and 🏳️‍🌈 $
+
+--- math-text-color ---
+// Test text properties.
+$text(#red, "time"^2) + sqrt("place")$
+
+--- math-font-features ---
+$ nothing $
+$ "hi ∅ hey" $
+$ sum_(i in NN) 1 + i $
+#show math.equation: set text(features: ("cv01",), fallback: false)
+$ nothing $
+$ "hi ∅ hey" $
+$ sum_(i in NN) 1 + i $
+
+--- math-optical-size-nested-scripts ---
+// Test transition from script to scriptscript.
+#[
+#set text(size:20pt)
+$ e^(e^(e^(e))) $
+]
+A large number: $e^(e^(e^(e)))$.
+
+--- math-optical-size-primes ---
+// Test prime/double prime via scriptsize
+#let prime = [ \u{2032} ]
+#let dprime = [ \u{2033} ]
+#let tprime = [ \u{2034} ]
+$ y^dprime-2y^prime + y = 0 $
+$y^dprime-2y^prime + y = 0$
+$ y^tprime_3 + g^(prime 2) $
+
+--- math-optical-size-prime-large-operator ---
+// Test prime superscript on large symbol
+$ scripts(sum_(k in NN))^prime 1/k^2 $
+$sum_(k in NN)^prime 1/k^2$
+
+--- math-optical-size-frac-script-script ---
+// Test script-script in a fraction.
+$ 1/(x^A) $
+#[#set text(size:18pt); $1/(x^A)$] vs. #[#set text(size:14pt); $x^A$]
diff --git a/tests/suite/math/underover.typ b/tests/suite/math/underover.typ
new file mode 100644
index 00000000..0768bf73
--- /dev/null
+++ b/tests/suite/math/underover.typ
@@ -0,0 +1,21 @@
+// Test under/over things.
+
+--- math-underover-brace ---
+// Test braces.
+$ x = underbrace(
+ 1 + 2 + ... + 5,
+ underbrace("numbers", x + y)
+) $
+
+--- math-underover-line-bracket ---
+// Test lines and brackets.
+$ x = overbracket(
+ overline(underline(x + y)),
+ 1 + 2 + ... + 5,
+) $
+
+--- math-underover-brackets ---
+// Test brackets.
+$ underbracket([1, 2/3], "relevant stuff")
+ arrow.l.r.double.long
+ overbracket([4/5,6], "irrelevant stuff") $
diff --git a/tests/suite/math/vec.typ b/tests/suite/math/vec.typ
new file mode 100644
index 00000000..312c0ee4
--- /dev/null
+++ b/tests/suite/math/vec.typ
@@ -0,0 +1,27 @@
+// Test vectors.
+
+--- math-vec-gap ---
+#set math.vec(gap: 1em)
+$ vec(1, 2) $
+
+
+--- math-vec-align-explicit-alternating ---
+// Test alternating alignment in a vector.
+$ vec(
+ "a" & "a a a" & "a a",
+ "a a" & "a a" & "a",
+ "a a a" & "a" & "a a a",
+) $
+
+--- math-vec-wide ---
+// Test wide cell.
+$ v = vec(1, 2+3, 4) $
+
+--- math-vec-delim-set ---
+// Test alternative delimiter.
+#set math.vec(delim: "[")
+$ vec(1, 2) $
+
+--- math-vec-delim-invalid ---
+// Error: 22-25 expected "(", "[", "{", "|", "||", or none
+#set math.vec(delim: "%")