diff --git a/tests/braille.rs b/tests/braille.rs
index 865f159b7..c494e8d54 100644
--- a/tests/braille.rs
+++ b/tests/braille.rs
@@ -13,6 +13,8 @@ mod braille {
}
mod UEB {
mod iceb;
+ mod ice_2026;
+ mod bana;
mod other;
}
diff --git a/tests/braille/UEB/bana.rs b/tests/braille/UEB/bana.rs
new file mode 100644
index 000000000..da0e51316
--- /dev/null
+++ b/tests/braille/UEB/bana.rs
@@ -0,0 +1,141 @@
+// UEB tests for GitHub issue #529, from the second (BANA) source document:
+// "Guidance on Transcribing Mathematics and Science in UEB", Section 5
+// "Preference on grade 1 indicators with technical expressions" (RUEB Section 5.9; GTM Section 1.7, 2025 update).
+//
+// Notes on scope:
+// - Examples 5-1 (x + y = 6), 5-7 (x^2 + y^2 = C), and 5-10 (a/b + c/d) are exact duplicates
+// of bana_5_1, bana_5_2, and bana_5_3 already in iceb.rs (same source examples, from the
+// 2019 BANA doc), so they aren't repeated here.
+// - Example 5-14 (a^n * a^m = a^(n+m)) uses the *same* MathML as the existing bana_5_4 test in
+// iceb.rs, but this document gives it *different* expected braille: two separate grade 1 word
+// indicators instead of one grade 1 passage. This is kept deliberately (as
+// bana2025_5_14_power_multiplication) as a direct old-vs-new contrast for issue #529 --
+// bana_5_4 documents the old (pre-2025) behavior, this documents the new one.
+// - Example 5-9 ("24. The slope of l1 is 5/3 and l1 || l2.") is skipped: it's not tagged to the
+// 2025 update, and the math is too interleaved with sentence prose to cleanly isolate per the
+// "math parts only" scoping used elsewhere.
+// - Examples 5-5 and 5-6 are matrices. The visual layout came through as garbled OCR text, so
+// the MathML here is a best-effort reconstruction from the (reliable, copy-pasted) braille
+// alone. If these fail in a way that doesn't look like an indicator-choice issue, check the
+// PDF directly before trusting the MathML.
+use crate::common::*;
+use anyhow::Result;
+
+// 5.1 No grade 1 indicators needed
+
+#[test]
+fn bana2025_5_2_surface_area() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠎⠥⠗⠋⠁⠉⠑⠀⠜⠑⠁⠀⠐⠶⠀⠼⠃⠇⠓⠐⠖⠼⠃⠇⠺⠐⠖⠼⠃⠺⠓")?;
+ return Ok(());
+}
+
+// 5.2 Grade 1 symbol indicators needed
+
+#[test]
+fn bana2025_5_3_nacl_h2o() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠝⠁⠠⠉⠇⠐⠖⠠⠓⠰⠢⠼⠃⠠⠕")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_4_fraction_5x2_over_y3z() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠷⠼⠑⠭⠔⠼⠃⠨⠌⠽⠔⠼⠉⠵⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_5_matrix_3x2() -> Result<()> {
+ // best-effort reconstruction -- see file header note
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠰⠃⠀⠉⠠⠐⠜⠠⠐⠣⠐⠤⠼⠁⠀⠼⠃⠀⠐⠤⠼⠉⠠⠐⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_6_matrix_2x2() -> Result<()> {
+ // best-effort reconstruction -- see file header note
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠨⠣⠁⠀⠰⠃⠀⠠⠨⠜⠠⠨⠣⠐⠤⠭⠀⠼⠃⠽⠠⠨⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_8_log_base2_plus_log() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠇⠕⠛⠰⠢⠼⠃⠭⠐⠖⠇⠕⠛⠀⠰⠭")?;
+ return Ok(());
+}
+
+// 5.3 Grade 1 word indicators
+
+#[test]
+fn bana2025_5_11_a_over_b() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠷⠁⠨⠌⠃⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_12_vector_ab() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠃⠜⠘⠱")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_13_x_inverse_eq_one_over_x() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠭⠔⠣⠐⠤⠼⠁⠜⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠭⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_14_power_multiplication() -> Result<()> {
+ // Same MathML as bana_5_4 in iceb.rs, but the 2025 update prefers two separate grade 1
+ // word indicators here instead of the single grade 1 passage bana_5_4 expects -- this is
+ // meant to directly contrast the old vs. new behavior for issue #529.
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠁⠔⠝⠐⠦⠁⠔⠍⠀⠐⠶⠀⠰⠰⠁⠔⠣⠝⠐⠖⠍⠜")?;
+ return Ok(());
+}
+
+// 5.4 Grade 1 passages
+
+#[test]
+fn bana2025_5_15_power_formula_passage() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠰⠠⠠⠠⠏⠀⠐⠶⠀⠧⠊⠀⠐⠶⠀⠊⠔⠼⠃⠗⠀⠐⠶⠀⠷⠧⠔⠼⠃⠨⠌⠗⠾⠠⠄⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn bana2025_5_16_equilibrium_reaction() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠰⠠⠉⠁⠠⠉⠠⠕⠢⠼⠉⠀⠳⠕⠻⠳⠪⠀⠠⠉⠁⠠⠕⠐⠖⠠⠉⠠⠕⠢⠼⠃⠰⠄")?;
+ return Ok(());
+}
diff --git a/tests/braille/UEB/ice_2026.rs b/tests/braille/UEB/ice_2026.rs
new file mode 100644
index 000000000..44355e40b
--- /dev/null
+++ b/tests/braille/UEB/ice_2026.rs
@@ -0,0 +1,572 @@
+// UEB tests for GitHub issue #529: align with the ICEB "Guidelines for Technical Material"
+// revision to Section 1.7 "Choice and placement of grade 1 indicators" (approved July 2025).
+//
+// Source: ICEB-Rules.pdf, Section 1, "1.7 Choice and placement of grade 1 indicators".
+//
+// Notes on scope:
+// - 1.7.1 ("Evaluate the following: 3 - 2 1/2 =") and 1.7.2 ("y = x + 4c") are already covered
+// by grade1_1_7_1 and grade1_1_7_2 in iceb.rs (those examples are unchanged from the older
+// ICEB document), so they are not repeated here.
+// - Per guidance, only the *math* part of each example is tested; generic English lead-in
+// text (e.g. "Evaluate the following:", "Solve the following quadratic equations:",
+// "Factorise:") is stripped since it isn't relevant to grade 1 indicator placement.
+// Words that are integral to the expression itself (e.g. "speed =", "Coordinate (x, y)",
+// "Assume g =") are kept, consistent with how existing tests (e.g. fraction_6_4_6,
+// expr_3_1_8) treat such labels.
+// - The 1.7.4 example (a numbered list of 3 exercises sharing one grade 1 passage, each on its
+// own print line) is not included. Two attempts at representing it as a single MathML
+// expression were tried and rejected: flat sibling / nodes separated by " "
+// produce plausible-looking braille, but the *speech* output ("...is equal to; 0 2 dot x
+// squared...") proves MathCAT doesn't actually see 3 separate expressions there -- the
+// passing braille was likely accidental. Wrapping each exercise in its own of an
+// gives correct, unambiguous speech, but triggers MathCAT's
+// matrix/system-of-equations braille formatting (extra "⠠⠠" row-separator indicators not
+// present in the source). There's no existing precedent elsewhere in this repo's tests for
+// representing "a numbered list of separate exercises" as one MathML expression, and neither
+// attempt faithfully reproduced the source, so this example is left untested rather than
+// testing a misrepresentation of it.
+// - The combined "Laws of indices" passage (page 11-12) is not included: it is one grade 1
+// passage spanning six separate equations across multiple print lines, and the correct
+// braille for any individual law depends on that surrounding passage context, which can't
+// be reconstructed reliably in isolation.
+// - Several expected braille strings below were reconstructed by joining lines that wrapped
+// across PDF pages/print lines. If one of these fails with only a stray extra/missing single
+// space, double check against the PDF source before assuming it's a MathCAT bug.
+// - Some of these tests are expected to fail against the current implementation -- that's the
+// point of issue #529. A failure with wildly more/fewer braille cells than expected likely
+// means the MathML (not MathCAT) is wrong and should be double checked.
+use crate::common::*;
+use anyhow::Result;
+
+// 1.7.3(a) allow one grade 1 symbol indicator per symbols-sequence
+
+#[test]
+fn grade1_1_7_3a_squared() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠔⠼⠃")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_3a_arrow_infinity() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠭⠀⠰⠳⠕⠀⠼⠿")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_3a_x_over_y() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠷⠭⠨⠌⠽⠰⠾ (word indicator misplaced after the open fraction sign)
+ test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾")?;
+ return Ok(());
+}
+
+// 1.7.3(b) use a grade 1 passage indicator if 3+ symbols-sequences need grade 1 indicators
+
+#[test]
+fn grade1_1_7_3b_subscripts_abc() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_3b_factorise_chain() -> Result<()> {
+ // "Factorise:" stripped -- see file header note on lead-in text
+ let expr = "";
+ test_braille("UEB", expr,
+ "⠰⠰⠰⠽⠀⠐⠶⠀⠭⠔⠼⠃⠐⠤⠼⠙⠆⠀⠽⠀⠐⠶⠀⠭⠔⠼⠃⠐⠤⠼⠃⠭⠆⠀⠽⠀⠐⠶⠀⠭⠐⠤⠭⠔⠼⠃⠲⠰⠄")?;
+ return Ok(());
+}
+
+// 1.7.4 -- just the 3 quadratic equations themselves, tested standalone (see file header note
+// on why the numbered-list-as-one-passage structure isn't included). Tested alone rather than
+// inside a passage, each needs its own inline grade 1 symbol indicator before the first x^2
+// (per 1.7.3(a)) -- the source's in-passage braille omits it because the surrounding passage
+// already covers it, so this expected value is derived from that rule, not copied verbatim.
+
+#[test]
+fn grade1_1_7_4_quadratic_1() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠭⠐⠤⠼⠃⠀⠐⠶⠀⠼⠚")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_4_quadratic_2() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠙⠭⠐⠤⠼⠉⠀⠐⠶⠀⠼⠚")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_4_quadratic_3() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠁⠀⠐⠶⠀⠼⠚")?;
+ return Ok(());
+}
+
+// 1.7.5 words which form part of a technical expression
+
+#[test]
+fn grade1_1_7_5a_speed_distance_time() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠷⠙⠊⠌⠨⠑⠨⠌⠐⠞⠰⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_5b_luminosity_sun() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠰⠇⠥⠍⠊⠝⠕⠎⠊⠞⠽⠢⠣⠎⠥⠝⠜ (word indicator placed at the very start
+ // rather than just before "sun", so "luminosity" can't appear contracted)
+ test_braille("UEB", expr, "⠇⠥⠍⠔⠕⠎⠰⠽⠰⠰⠢⠣⠎⠥⠝⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_5c_speed_chain() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr,
+ "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠀⠐⠶⠀⠷⠼⠉⠚⠂⠚⠚⠚⠀⠍⠨⠌⠼⠋⠚⠀⠎⠾⠀⠐⠶⠀⠼⠑⠚⠚⠀⠍⠸⠌⠎⠰⠄")?;
+ return Ok(());
+}
+
+// 1.7.9 further examples of preferred grade 1 indicator usage
+
+#[test]
+fn grade1_1_7_9_01_y_eq_x() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠭")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_02_sqrt25() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠩⠼⠃⠑⠬⠀⠐⠶⠀⠼⠑")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_03_sqrtx() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠩⠭⠬⠀⠐⠶⠀⠼⠛")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_04_ms_inverse() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠍⠎⠰⠔⠰⠣⠐⠤⠼⠁⠜ [or] ⠍⠎⠰⠰⠔⠣⠐⠤⠼⠁⠜
+ test_braille("UEB", expr, "⠰⠰⠍⠎⠔⠣⠐⠤⠼⠁⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_05_y_eq_x_over_2() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠷⠭⠨⠌⠼⠃⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_06_y_eq_xsq_over_2() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠰⠷⠭⠔⠼⠃⠨⠌⠼⠃⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_07_x_over_y_eq_c_over_d() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾⠀⠐⠶⠀⠰⠰⠷⠉⠨⠌⠙⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_08_coordinate_xy() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠉⠕⠕⠗⠙⠔⠁⠞⠑⠀⠐⠣⠰⠭⠂⠀⠰⠽⠐⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_09_coordinate_xi_yi() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠉⠕⠕⠗⠙⠔⠁⠞⠑⠀⠐⠣⠭⠰⠢⠊⠂⠀⠽⠰⠢⠊⠐⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_10_coordinate_xi2_yi2() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠉⠕⠕⠗⠙⠔⠁⠞⠑⠀⠰⠰⠐⠣⠭⠢⠊⠔⠼⠃⠂⠀⠰⠰⠽⠢⠊⠔⠼⠃⠐⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_11_b_bar() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠃⠰⠱")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_12_m_leftright_arrow() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠍⠨⠔⠰⠳⠺⠗⠕")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_13_az_leftright_arrow() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠣⠠⠠⠁⠵⠰⠜⠨⠔⠰⠳⠺⠗⠕
+ test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠵⠜⠨⠔⠳⠺⠗⠕")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_14_al_isotope() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠢⠼⠁⠉⠔⠼⠃⠛⠠⠁⠇")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_15_o_2minus() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠠⠕⠔⠣⠼⠃⠐⠤⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_16_chem_h2_o2_reaction() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr,
+ "⠼⠃⠠⠓⠢⠼⠃⠐⠣⠛⠐⠜⠐⠖⠠⠕⠢⠼⠃⠐⠣⠛⠐⠜⠀⠰⠳⠕⠀⠼⠃⠠⠓⠢⠼⠃⠠⠕⠐⠣⠇⠐⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_17_y_eq_mx_c() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠍⠭⠐⠖⠉")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_18_a_eq_pi_r_sq() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠁⠀⠐⠶⠀⠨⠏⠗⠰⠔⠼⠃")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_19_e_eq_mc2() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠠⠑⠀⠐⠶⠀⠍⠉⠰⠔⠼⠃")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_20_perfect_square() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠃⠭⠐⠖⠼⠁⠀⠐⠶⠀⠐⠣⠭⠐⠤⠼⠁⠐⠜⠔⠼⠃")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_21_xn_alternating() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠼⠁⠐⠖⠷⠐⠤⠼⠁⠔⠝⠨⠌⠝⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_22_xn_exponential() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠰⠷⠼⠃⠔⠝⠨⠌⠝⠔⠼⠃⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_23_quadratic_formula() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_24_x_sqrt_sintheta() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠭⠰⠩⠎⠔⠨⠹⠰⠬
+ test_braille("UEB", expr, "⠰⠰⠭⠩⠎⠊⠝⠨⠹⠬")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_25_version_1b() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠰⠠⠧⠑⠗⠎⠊⠕⠝⠔⠣⠼⠁⠰⠃⠜
+ test_braille("UEB", expr, "⠠⠧⠻⠨⠝⠰⠰⠔⠣⠼⠁⠰⠃⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_26_w_eq_fs() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠠⠺⠀⠐⠶⠀⠠⠋⠎")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_27_work_eq_force_times_distance() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠐⠺⠀⠐⠶⠀⠿⠉⠑⠐⠦⠙⠊⠌⠨⠑")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_28_therefore_force() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠠⠡⠀⠿⠉⠑⠀⠐⠶⠀⠰⠷⠐⠺⠨⠌⠙⠊⠌⠨⠑⠰⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_29_acceleration() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠁⠒⠑⠇⠻⠁⠰⠝⠀⠐⠶⠀⠰⠷⠠⠨⠙⠎⠏⠑⠫⠨⠌⠠⠨⠙⠐⠞⠰⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_30_frequency_fraction() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠞⠊⠍⠑⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_31_frequency_slash() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠼⠁⠸⠌⠞⠊⠍⠑")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_32_vcone() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠠⠧⠰⠢⠰⠣⠉⠐⠕⠰⠜⠀⠐⠶⠀⠼⠁⠌⠉⠨⠏⠗⠔⠼⠃⠰⠓
+ test_braille("UEB", expr, "⠰⠰⠠⠧⠢⠣⠉⠕⠝⠑⠜⠀⠐⠶⠀⠼⠁⠌⠉⠨⠏⠗⠔⠼⠃⠰⠓")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_33_v_integral() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠰⠰⠠⠧⠀⠐⠶⠀⠮⠨⠏⠽⠔⠼⠃⠰⠙⠭⠰⠄
+ test_braille("UEB", expr, "⠰⠠⠧⠀⠐⠶⠀⠰⠰⠮⠨⠏⠽⠔⠼⠃⠰⠙⠭")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_34_period() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠏⠻⠊⠕⠙⠀⠐⠶⠀⠼⠃⠨⠏⠩⠷⠇⠀⠰⠰⠉⠕⠎⠨⠁⠨⠌⠛⠾⠬")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_35_assume_g() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠠⠁⠎⠎⠥⠍⠑⠀⠰⠛⠀⠐⠶⠀⠼⠊⠲⠓⠁⠀⠰⠰⠍⠎⠔⠣⠐⠤⠼⠃⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_36_v1_x_ms() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠰⠧⠢⠼⠁⠀⠐⠶⠀⠭⠀⠍⠎⠔⠣⠐⠤⠼⠁⠜⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_37_p_set() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠠⠏⠀⠐⠶⠀⠸⠣⠰⠞⠂⠀⠰⠥⠂⠀⠰⠧⠸⠜
+ test_braille("UEB", expr, "⠰⠰⠰⠠⠏⠀⠐⠶⠀⠸⠣⠞⠂⠀⠥⠂⠀⠧⠸⠜⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_38_four_subscripts() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠭⠰⠢⠁⠀⠐⠶⠀⠭⠰⠢⠃⠀⠐⠶⠀⠭⠰⠢⠉⠀⠐⠶⠀⠭⠰⠢⠙
+ test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠀⠐⠶⠀⠭⠢⠙⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_39_power_multiplication() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠰⠭⠔⠼⠃⠐⠦⠭⠔⠼⠉⠀⠐⠶⠀⠭⠔⠣⠼⠃⠐⠖⠼⠉⠜⠀⠐⠶⠀⠭⠔⠼⠑⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_40_b_arrow_c() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠠⠃⠀⠰⠳⠕⠀⠰⠠⠉
+ test_braille("UEB", expr, "⠰⠰⠰⠠⠃⠀⠳⠕⠀⠠⠉⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_41_x_plus_y_eq_5() -> Result<()> {
+ let expr = "";
+ // [rather than] ⠰⠰⠰⠭⠐⠖⠽⠀⠐⠶⠀⠼⠑⠰⠄
+ test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠼⠑")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_42_methane_chlorine() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr,
+ "⠰⠰⠰⠠⠉⠠⠓⠢⠼⠙⠐⠖⠼⠙⠠⠉⠇⠢⠼⠃⠀⠳⠕⠀⠠⠉⠠⠉⠇⠢⠼⠙⠐⠖⠼⠙⠠⠓⠠⠉⠇⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_43_carbon_decay() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr,
+ "⠰⠰⠰⠢⠼⠋⠔⠼⠁⠙⠠⠉⠀⠳⠕⠀⠢⠼⠛⠔⠼⠁⠙⠠⠝⠐⠖⠢⠣⠐⠤⠼⠁⠜⠔⠼⠚⠨⠃⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_44_reliability() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr,
+ "⠗⠑⠇⠊⠁⠃⠊⠇⠰⠽⠀⠐⠶⠀⠰⠰⠰⠷⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠋⠁⠥⠇⠞⠎⠨⠌⠞⠕⠞⠁⠇⠀⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠊⠞⠑⠍⠎⠾⠀⠐⠶⠀⠏⠰⠄")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_45_law_of_indices_a0() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠁⠰⠔⠼⠚⠀⠐⠶⠀⠼⠁")?;
+ return Ok(());
+}
+
+// The remaining 4 laws are extrapolated from two confirmed anchors (bana2025_5_13's x^-1=1/x,
+// bana2025_5_14's a^n*a^m=a^(n+m)), using two hypotheses that aren't independently verified:
+// (1) 2+ distinct letters in one symbols-sequence -> word tier, not just symbol tier, and
+// (2) a negative sign inside an exponent escalates to word tier even with only 1 letter present.
+// Treat any failure here with suspicion of the *expected* value, not just the implementation --
+// unlike the quadratics/law-of-a^0 tests, these aren't independently confirmed from source.
+
+#[test]
+fn grade1_1_7_9_46_law_of_indices_power_of_power() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠐⠣⠁⠔⠍⠐⠜⠔⠝⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠝⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_47_law_of_indices_division() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠷⠁⠔⠍⠨⠌⠁⠔⠝⠾⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠐⠤⠝⠜")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_48_law_of_indices_negative_power() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠁⠔⠣⠐⠤⠍⠜⠀⠐⠶⠀⠰⠰⠷⠼⠁⠨⠌⠁⠔⠍⠾")?;
+ return Ok(());
+}
+
+#[test]
+fn grade1_1_7_9_49_law_of_indices_root() -> Result<()> {
+ let expr = "";
+ test_braille("UEB", expr, "⠰⠰⠁⠔⠷⠼⠁⠨⠌⠝⠾⠀⠐⠶⠀⠰⠰⠩⠔⠝⠁⠬")?;
+ return Ok(());
+}