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 = "Surface area= + 2lh+ + 2lw+ + 2wh"; + test_braille("UEB", expr, "⠠⠎⠥⠗⠋⠁⠉⠑⠀⠜⠑⠁⠀⠐⠶⠀⠼⠃⠇⠓⠐⠖⠼⠃⠇⠺⠐⠖⠼⠃⠺⠓")?; + return Ok(()); +} + +// 5.2 Grade 1 symbol indicators needed + +#[test] +fn bana2025_5_3_nacl_h2o() -> Result<()> { + let expr = "NaCl+ + H2O"; + test_braille("UEB", expr, "⠠⠝⠁⠠⠉⠇⠐⠖⠠⠓⠰⠢⠼⠃⠠⠕")?; + return Ok(()); +} + +#[test] +fn bana2025_5_4_fraction_5x2_over_y3z() -> Result<()> { + let expr = " + 5x2 + y3z + "; + test_braille("UEB", expr, "⠰⠷⠼⠑⠭⠔⠼⠃⠨⠌⠽⠔⠼⠉⠵⠾")?; + return Ok(()); +} + +#[test] +fn bana2025_5_5_matrix_3x2() -> Result<()> { + // best-effort reconstruction -- see file header note + let expr = "( + abc + -12-3 + )"; + test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠰⠃⠀⠉⠠⠐⠜⠠⠐⠣⠐⠤⠼⠁⠀⠼⠃⠀⠐⠤⠼⠉⠠⠐⠜")?; + return Ok(()); +} + +#[test] +fn bana2025_5_6_matrix_2x2() -> Result<()> { + // best-effort reconstruction -- see file header note + let expr = "[ + ab + -x2y + ]"; + test_braille("UEB", expr, "⠠⠨⠣⠁⠀⠰⠃⠀⠠⠨⠜⠠⠨⠣⠐⠤⠭⠀⠼⠃⠽⠠⠨⠜")?; + return Ok(()); +} + +#[test] +fn bana2025_5_8_log_base2_plus_log() -> Result<()> { + let expr = "log2x+logx"; + test_braille("UEB", expr, "⠇⠕⠛⠰⠢⠼⠃⠭⠐⠖⠇⠕⠛⠀⠰⠭")?; + return Ok(()); +} + +// 5.3 Grade 1 word indicators + +#[test] +fn bana2025_5_11_a_over_b() -> Result<()> { + let expr = "ab"; + test_braille("UEB", expr, "⠰⠰⠷⠁⠨⠌⠃⠾")?; + return Ok(()); +} + +#[test] +fn bana2025_5_12_vector_ab() -> Result<()> { + let expr = "AB + "; + test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠃⠜⠘⠱")?; + return Ok(()); +} + +#[test] +fn bana2025_5_13_x_inverse_eq_one_over_x() -> Result<()> { + let expr = "x-1= + 1x"; + 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 = "an×am= + an+m"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠝⠐⠦⠁⠔⠍⠀⠐⠶⠀⠰⠰⠁⠔⠣⠝⠐⠖⠍⠜")?; + return Ok(()); +} + +// 5.4 Grade 1 passages + +#[test] +fn bana2025_5_15_power_formula_passage() -> Result<()> { + let expr = "P= + VI= + I2R= + V2R"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠠⠠⠏⠀⠐⠶⠀⠧⠊⠀⠐⠶⠀⠊⠔⠼⠃⠗⠀⠐⠶⠀⠷⠧⠔⠼⠃⠨⠌⠗⠾⠠⠄⠰⠄")?; + return Ok(()); +} + +#[test] +fn bana2025_5_16_equilibrium_reaction() -> Result<()> { + let expr = "CaCO3 + + CaO+ + CO2"; + 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 = "x2"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_3a_arrow_infinity() -> Result<()> { + let expr = "x"; + test_braille("UEB", expr, "⠰⠭⠀⠰⠳⠕⠀⠼⠿")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_3a_x_over_y() -> Result<()> { + let expr = "xy"; + // [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 = "xa= + xb= + xc"; + 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 = " + y=x24; + y=x22x; + y=xx2. + "; + 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 = "x2x2=0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠭⠐⠤⠼⠃⠀⠐⠶⠀⠼⠚")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_4_quadratic_2() -> Result<()> { + let expr = "x24x3=0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠙⠭⠐⠤⠼⠉⠀⠐⠶⠀⠼⠚")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_4_quadratic_3() -> Result<()> { + let expr = "x21=0"; + 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 = "speed= + distancetime"; + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠷⠙⠊⠌⠨⠑⠨⠌⠐⠞⠰⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_5b_luminosity_sun() -> Result<()> { + let expr = "luminositysun"; + // [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 = "speed= + distancetime= + 30,000 m + 60 s= + 500 m/s"; + 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 = "y=x"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠭")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_02_sqrt25() -> Result<()> { + let expr = "25=5"; + test_braille("UEB", expr, "⠰⠩⠼⠃⠑⠬⠀⠐⠶⠀⠼⠑")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_03_sqrtx() -> Result<()> { + let expr = "x=7"; + test_braille("UEB", expr, "⠰⠰⠩⠭⠬⠀⠐⠶⠀⠼⠛")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_04_ms_inverse() -> Result<()> { + let expr = "ms + -1"; + // [rather than] ⠍⠎⠰⠔⠰⠣⠐⠤⠼⠁⠜ [or] ⠍⠎⠰⠰⠔⠣⠐⠤⠼⠁⠜ + test_braille("UEB", expr, "⠰⠰⠍⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_05_y_eq_x_over_2() -> Result<()> { + let expr = "y=x2"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠷⠭⠨⠌⠼⠃⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_06_y_eq_xsq_over_2() -> Result<()> { + let expr = "y= + x22"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠰⠷⠭⠔⠼⠃⠨⠌⠼⠃⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_07_x_over_y_eq_c_over_d() -> Result<()> { + let expr = "xy= + cd"; + test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾⠀⠐⠶⠀⠰⠰⠷⠉⠨⠌⠙⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_08_coordinate_xy() -> Result<()> { + let expr = "Coordinate (x,y)"; + test_braille("UEB", expr, "⠠⠉⠕⠕⠗⠙⠔⠁⠞⠑⠀⠐⠣⠰⠭⠂⠀⠰⠽⠐⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_09_coordinate_xi_yi() -> Result<()> { + let expr = "Coordinate ( + xi, + yi)"; + test_braille("UEB", expr, "⠠⠉⠕⠕⠗⠙⠔⠁⠞⠑⠀⠐⠣⠭⠰⠢⠊⠂⠀⠽⠰⠢⠊⠐⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_10_coordinate_xi2_yi2() -> Result<()> { + let expr = "Coordinate ( + xi2, + yi2)"; + test_braille("UEB", expr, "⠠⠉⠕⠕⠗⠙⠔⠁⠞⠑⠀⠰⠰⠐⠣⠭⠢⠊⠔⠼⠃⠂⠀⠰⠰⠽⠢⠊⠔⠼⠃⠐⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_11_b_bar() -> Result<()> { + let expr = "B¯"; + test_braille("UEB", expr, "⠠⠃⠰⠱")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_12_m_leftright_arrow() -> Result<()> { + let expr = "M"; + test_braille("UEB", expr, "⠠⠍⠨⠔⠰⠳⠺⠗⠕")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_13_az_leftright_arrow() -> Result<()> { + let expr = "AZ + "; + // [rather than] ⠰⠣⠠⠠⠁⠵⠰⠜⠨⠔⠰⠳⠺⠗⠕ + test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠵⠜⠨⠔⠳⠺⠗⠕")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_14_al_isotope() -> Result<()> { + let expr = "Al + 1327"; + test_braille("UEB", expr, "⠰⠢⠼⠁⠉⠔⠼⠃⠛⠠⠁⠇")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_15_o_2minus() -> Result<()> { + let expr = "O2-"; + test_braille("UEB", expr, "⠰⠰⠠⠕⠔⠣⠼⠃⠐⠤⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_16_chem_h2_o2_reaction() -> Result<()> { + let expr = " + 2H2(g) + +O2(g) + + 2H2O(l) + "; + test_braille("UEB", expr, + "⠼⠃⠠⠓⠢⠼⠃⠐⠣⠛⠐⠜⠐⠖⠠⠕⠢⠼⠃⠐⠣⠛⠐⠜⠀⠰⠳⠕⠀⠼⠃⠠⠓⠢⠼⠃⠠⠕⠐⠣⠇⠐⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_17_y_eq_mx_c() -> Result<()> { + let expr = "y=mx+c"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠍⠭⠐⠖⠉")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_18_a_eq_pi_r_sq() -> Result<()> { + let expr = "A= + πr2"; + test_braille("UEB", expr, "⠠⠁⠀⠐⠶⠀⠨⠏⠗⠰⠔⠼⠃")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_19_e_eq_mc2() -> Result<()> { + let expr = "E= + mc2"; + test_braille("UEB", expr, "⠰⠠⠑⠀⠐⠶⠀⠍⠉⠰⠔⠼⠃")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_20_perfect_square() -> Result<()> { + let expr = "x22x+1 + =(x1)2"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠃⠭⠐⠖⠼⠁⠀⠐⠶⠀⠐⠣⠭⠐⠤⠼⠁⠐⠜⠔⠼⠃")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_21_xn_alternating() -> Result<()> { + let expr = "xn=1+ + (1)nn"; + test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠼⠁⠐⠖⠷⠐⠤⠼⠁⠔⠝⠨⠌⠝⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_22_xn_exponential() -> Result<()> { + let expr = "xn= + 2nn2"; + test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠰⠷⠼⠃⠔⠝⠨⠌⠝⠔⠼⠃⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_23_quadratic_formula() -> Result<()> { + let expr = "x= + + b± + b24ac + + 2a + + "; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_24_x_sqrt_sintheta() -> Result<()> { + let expr = "xsinθ"; + // [rather than] ⠭⠰⠩⠎⠔⠨⠹⠰⠬ + test_braille("UEB", expr, "⠰⠰⠭⠩⠎⠊⠝⠨⠹⠬")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_25_version_1b() -> Result<()> { + let expr = "Version1b"; + // [rather than] ⠰⠰⠠⠧⠑⠗⠎⠊⠕⠝⠔⠣⠼⠁⠰⠃⠜ + test_braille("UEB", expr, "⠠⠧⠻⠨⠝⠰⠰⠔⠣⠼⠁⠰⠃⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_26_w_eq_fs() -> Result<()> { + let expr = "W= + Fs"; + test_braille("UEB", expr, "⠰⠠⠺⠀⠐⠶⠀⠠⠋⠎")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_27_work_eq_force_times_distance() -> Result<()> { + let expr = "work=force×distance"; + test_braille("UEB", expr, "⠐⠺⠀⠐⠶⠀⠿⠉⠑⠐⠦⠙⠊⠌⠨⠑")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_28_therefore_force() -> Result<()> { + let expr = "force= + workdistance"; + test_braille("UEB", expr, "⠰⠠⠡⠀⠿⠉⠑⠀⠐⠶⠀⠰⠷⠐⠺⠨⠌⠙⠊⠌⠨⠑⠰⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_29_acceleration() -> Result<()> { + let expr = "acceleration= + Δspeed + Δtime"; + test_braille("UEB", expr, "⠁⠒⠑⠇⠻⠁⠰⠝⠀⠐⠶⠀⠰⠷⠠⠨⠙⠎⠏⠑⠫⠨⠌⠠⠨⠙⠐⠞⠰⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_30_frequency_fraction() -> Result<()> { + let expr = "frequency=1time"; + test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠞⠊⠍⠑⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_31_frequency_slash() -> Result<()> { + let expr = "frequency=1/time"; + test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠼⠁⠸⠌⠞⠊⠍⠑")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_32_vcone() -> Result<()> { + let expr = "Vcone= + 13 + πr2h"; + // [rather than] ⠠⠧⠰⠢⠰⠣⠉⠐⠕⠰⠜⠀⠐⠶⠀⠼⠁⠌⠉⠨⠏⠗⠔⠼⠃⠰⠓ + test_braille("UEB", expr, "⠰⠰⠠⠧⠢⠣⠉⠕⠝⠑⠜⠀⠐⠶⠀⠼⠁⠌⠉⠨⠏⠗⠔⠼⠃⠰⠓")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_33_v_integral() -> Result<()> { + let expr = "V= + π + y2dx"; + // [rather than] ⠰⠰⠰⠠⠧⠀⠐⠶⠀⠮⠨⠏⠽⠔⠼⠃⠰⠙⠭⠰⠄ + test_braille("UEB", expr, "⠰⠠⠧⠀⠐⠶⠀⠰⠰⠮⠨⠏⠽⠔⠼⠃⠰⠙⠭")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_34_period() -> Result<()> { + let expr = "period=2π + lcosαg"; + test_braille("UEB", expr, "⠏⠻⠊⠕⠙⠀⠐⠶⠀⠼⠃⠨⠏⠩⠷⠇⠀⠰⠰⠉⠕⠎⠨⠁⠨⠌⠛⠾⠬")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_35_assume_g() -> Result<()> { + let expr = "Assume g=9.81  + ms-2"; + test_braille("UEB", expr, "⠠⠁⠎⠎⠥⠍⠑⠀⠰⠛⠀⠐⠶⠀⠼⠊⠲⠓⠁⠀⠰⠰⠍⠎⠔⠣⠐⠤⠼⠃⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_36_v1_x_ms() -> Result<()> { + let expr = "v1=x  + ms-1"; + test_braille("UEB", expr, "⠰⠰⠰⠧⠢⠼⠁⠀⠐⠶⠀⠭⠀⠍⠎⠔⠣⠐⠤⠼⠁⠜⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_37_p_set() -> Result<()> { + let expr = "P= + {t,u,v}"; + // [rather than] ⠰⠠⠏⠀⠐⠶⠀⠸⠣⠰⠞⠂⠀⠰⠥⠂⠀⠰⠧⠸⠜ + test_braille("UEB", expr, "⠰⠰⠰⠠⠏⠀⠐⠶⠀⠸⠣⠞⠂⠀⠥⠂⠀⠧⠸⠜⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_38_four_subscripts() -> Result<()> { + let expr = "xa= + xb= + xc= + xd"; + // [rather than] ⠭⠰⠢⠁⠀⠐⠶⠀⠭⠰⠢⠃⠀⠐⠶⠀⠭⠰⠢⠉⠀⠐⠶⠀⠭⠰⠢⠙ + test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠀⠐⠶⠀⠭⠢⠙⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_39_power_multiplication() -> Result<()> { + let expr = "x2×x3= + x2+3= + x5"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠔⠼⠃⠐⠦⠭⠔⠼⠉⠀⠐⠶⠀⠭⠔⠣⠼⠃⠐⠖⠼⠉⠜⠀⠐⠶⠀⠭⠔⠼⠑⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_40_b_arrow_c() -> Result<()> { + let expr = "BC"; + // [rather than] ⠰⠠⠃⠀⠰⠳⠕⠀⠰⠠⠉ + test_braille("UEB", expr, "⠰⠰⠰⠠⠃⠀⠳⠕⠀⠠⠉⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_41_x_plus_y_eq_5() -> Result<()> { + let expr = "x+y=5"; + // [rather than] ⠰⠰⠰⠭⠐⠖⠽⠀⠐⠶⠀⠼⠑⠰⠄ + test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠼⠑")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_42_methane_chlorine() -> Result<()> { + let expr = " + CH4 + +4Cl2 + + CCl4 + +4HCl + "; + test_braille("UEB", expr, + "⠰⠰⠰⠠⠉⠠⠓⠢⠼⠙⠐⠖⠼⠙⠠⠉⠇⠢⠼⠃⠀⠳⠕⠀⠠⠉⠠⠉⠇⠢⠼⠙⠐⠖⠼⠙⠠⠓⠠⠉⠇⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_43_carbon_decay() -> Result<()> { + let expr = " + C614 + + N714 + + + β-10 + "; + test_braille("UEB", expr, + "⠰⠰⠰⠢⠼⠋⠔⠼⠁⠙⠠⠉⠀⠳⠕⠀⠢⠼⠛⠔⠼⠁⠙⠠⠝⠐⠖⠢⠣⠐⠤⠼⠁⠜⠔⠼⠚⠨⠃⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_44_reliability() -> Result<()> { + let expr = "reliability= + number of faultstotal number of items + =p"; + test_braille("UEB", expr, + "⠗⠑⠇⠊⠁⠃⠊⠇⠰⠽⠀⠐⠶⠀⠰⠰⠰⠷⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠋⠁⠥⠇⠞⠎⠨⠌⠞⠕⠞⠁⠇⠀⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠊⠞⠑⠍⠎⠾⠀⠐⠶⠀⠏⠰⠄")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_45_law_of_indices_a0() -> Result<()> { + let expr = "a0=1"; + 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 = "(am)n= + amn"; + test_braille("UEB", expr, "⠰⠰⠐⠣⠁⠔⠍⠐⠜⠔⠝⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠝⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_47_law_of_indices_division() -> Result<()> { + let expr = "aman= + amn"; + test_braille("UEB", expr, "⠰⠰⠷⠁⠔⠍⠨⠌⠁⠔⠝⠾⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠐⠤⠝⠜")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_48_law_of_indices_negative_power() -> Result<()> { + let expr = "am= + 1am"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠣⠐⠤⠍⠜⠀⠐⠶⠀⠰⠰⠷⠼⠁⠨⠌⠁⠔⠍⠾")?; + return Ok(()); +} + +#[test] +fn grade1_1_7_9_49_law_of_indices_root() -> Result<()> { + let expr = "a1n= + an"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠷⠼⠁⠨⠌⠝⠾⠀⠐⠶⠀⠰⠰⠩⠔⠝⠁⠬")?; + return Ok(()); +}