Add Polish math braille code - #764
Open
michaldziwisz wants to merge 11 commits into
Open
Conversation
Implements the Polish braille notation for mathematics, physics and chemistry as a new braille code, so a Polish reader can get maths in the notation their textbooks and examination papers actually use. Source: "Brajlowska notacja matematyczna, fizyczna, chemiczna", 2nd edition, Krakow-Laski-Lodz 2011, recommended by the Polish Ministry of National Education. The code descends from H. Epheser, "Internationale Mathematikschrift fuer Blinde" (Marburg), so it is a relative of the German notation rather than of UEB or Nemeth. Every cell pattern was measured from that document, not guessed. The guide prints braille with a 6-dot font, so the cells were recovered by rendering each glyph and reading which of the six positions are inked; each entry carries the page it came from. What is covered - digits, the full Polish alphabet with diacritics, capitals - Greek letters, lower and upper case (48 letters) - operators, relations and their negations - roots, short-form fractions - exponents and indices with dropped digits, compound projectors - trigonometric and inverse trigonometric function names - angle units, derivatives, integrals - plane figures, set theory, logic, limits - units of measure, chemical elements and molecules What is not covered - the precedence between the six spacing groups (guide p. 58-59). The group-B blank before relations and binary operators is implemented; resolving conflicts between groups is not, and the definitions file says so explicitly. - semi-structural and structural chemistry formulae (p. 92-101), which are two-dimensional drawings and outside a linear MathML translation. Tests: 31 new tests in tests/braille/Polish/polish.rs, each expectation taken from an example printed in the guide. Full braille suite passes with no regressions (2013 passed, 0 failed; 1982 before this change, the difference being exactly the new tests).
…perators
A coverage probe (temporarily printing the engine's output instead of asserting)
showed that several common MathML tags had no Polish rule at all:
mover / munder / munderover / mtable -> "Pattern match/replacement failure!"
|a| -> the ASCII bar leaked into the braille
sum with limits -> the code point was spelled out
Each fix is read off the guide:
absolute value (p. 12) distinct opening/closing signs, so it cannot be a
unicode.yaml entry for a single character
vector (p. 49) the arrow key sign brackets the whole expression
limit (p. 56) lim is number-sign + l; the limit is a lower index
matrix (p. 46) bracketed, entries and rows separated by a blank
sum, product the guide has no dedicated sign and lists only the
Greek capitals (p. 7), so these map onto capital
sigma and pi, as Swedish and UEB also do
mmultiscripts gets a rule for the common one-index-each-side shape so it no
longer fails outright; the full left-index grammar of p. 65 is still open, and
menclose passes its content through because the guide has no sign for it.
Polish braille tests: 31 -> 36. Full braille suite 2018 passed, 0 failed.
More cases taken from the guide's own worked examples (pp. 5, 25), the same way
the Swedish tests follow their manual:
decimal comma the guide uses ONLY the comma, never the point (p. 5), and its
examples read 7,29 and 0,072 - the entry was missing entirely
per cent and per mille, written after the number (p. 5)
mode signs the number sign and the letter sign are NOT repeated after a
separator inside one construct: the guide writes 7,29 with one
number sign and x/y with one letter sign, but the engine was
emitting both twice
The comma needed the N flag so it does not terminate numeric mode, and mfrac now
strips the letter sign from the denominator.
Polish braille tests: 36 -> 39. Full braille suite 2021 passed, 0 failed.
The guide classifies every sign TWICE (pp. 3, 58-59): once for its left-hand side (groups A/B/C) and once for its right (A'/B'/C'). Relations and binary operators are group B - written with a blank cell on the LEFT - and what follows them is decided by the NEXT sign's own group, with rule 1 (p. 59) removing the gap when that sign is from group C. We were emitting the blank on BOTH sides. Measured over the whole guide by reading the braille glyphs (941 occurrences): equals 383 cases blank on the left 78% on the right 3% plus 262 cases left 58% right 9% minus 296 cases left 45% right 9% Counting what actually follows a relation (402 cases) explains the asymmetry: the most common successor is the number sign (29%), then the letter and capital signs - exactly the group C/A behaviour rule 1 describes. Five existing tests encoded the wrong output and were corrected. Also adds the ASCII hyphen-minus U+002D, which had no entry and reached the braille output as a raw ASCII byte, and a test for rule 1 via the prime (group C). Full conflict resolution across all six groups is still not implemented; the comment in the spacing rule says so. Polish braille tests: 39 -> 41. Full braille suite 2023 passed, 0 failed.
The guide devotes a section to brackets (p. 11) and gives four pairs - round, square, curly and angle - plus literary brackets for non-mathematical text (p. 12). None of them had an entry. The symptom was misleading rather than absent: brackets came out as "(a)" -> digit cells, because the expression fell through to the digit table used for indices. Non-empty output is not correct output. Roman numerals are deliberately NOT handled. Page 6 writes them with a single capital sign for the whole run, but p. 57 gives the general rule - "w granicach symbolu kazdy znak duzej litery odnosi sie tylko do tej litery przed ktora stoi" - and the guide's own PLN, geometry points and vectors repeat the sign. Roman numerals are therefore an exception that needs to be recognised as such, which MathCAT cannot do today; collapsing every capital run breaks ordinary uses, so the limitation is documented in the test file instead. Polish braille tests: 41 -> 43. Full braille suite 2025 passed, 0 failed.
…umbers
Four more signs the guide defines that had no entry at all, so their code points
leaked into the braille output cell by cell:
divides / does not divide (p. 12) - the negation adds the same dots 3-5 cell
that negates any relation (p. 58)
multiplication dot (p. 10)
semicolon, colon, question mark, exclamation mark (p. 13)
Note the divisibility sign shares its cells with the OPENING absolute value
(p. 12); the guide reuses the sign and lets position disambiguate. Here there is
no clash because the absolute-value rule matches an mrow of exactly three children
with '|' on both sides.
The two signs needed different answers on spacing, both taken from the guide:
divisibility takes a blank on the left (its example has one, so it joins the
group-B list), while the multiplication dot does NOT, because p. 10 makes it an
explicit exception - "wyjatek stanowi znak mnozenia, ktory moze byc pisany
dwojako" - and prints both forms. The test says which form we emit and why.
Polish braille tests: 43 -> 47. Full braille suite 2029 passed, 0 failed.
…tive log and ln were being spelled out letter by letter because they were missing from the function-name rule. Measured off the guide's glyphs (p. 47): log is the function prefix plus one cell, ln puts an extra cell between them, matching its worked examples log 1000 = 3 and log2 16 = 4. The factorial (p. 55), integral and partial derivative (p. 57) already produced the right cells, but only as a side effect of entries added for other sections - no test covered them. They have tests now so a later change cannot break them silently. The logarithm BASE is deliberately not implemented: p. 47 makes it a left-hand superscript written BEFORE the function name (log2 16 reads base-marker, 2, log, 16), which needs the MathML children reordered rather than a new entry - the same class as the left-hand indices of p. 65. Polish braille tests: 47 -> 51. Full braille suite 2033 passed, 0 failed.
…ube root The mroot rule emitted a hard-coded pair of cells before the degree, so the degree was stated twice and inconsistently: the fifth root of 32 came out as the cells for 3 followed by 5. The pair was a misreading of the guide's own notation. Dots 3-4 alone are the upper-left-index key sign; the second cell in its p. 36 example is the LOWERED digit 3 - that example's degree. The guide's second example settles it: the nth root of x puts a letter exactly where that digit was. Whole numbers in an index also use lowered digits with no number sign (p. 33 rule 1), which is what makes the terminator unnecessary for a root degree (p. 36 rule 5). The rule now reuses the same lowered-digit table as the power and subscript rules. This survived sixteen batches because no test covered mroot at all - msqrt had one from the start and passed, so "roots work" looked verified. They are two separate rules. The logarithm BASE is still not implemented (p. 36 rule 6): MathML hands it over as an msub whose base is the function name, so it needs the children reordered, and that touches the msub rule shared by every right-hand index. Polish braille tests: 51 -> 54. Full braille suite 2036 passed, 0 failed.
Page 36 rule 6: a logarithm base, printed as a lower index after the function name, is written in braille as an upper LEFT index - before it. The guide's worked example on p. 47 reads log2 8 = 3 as key sign, lowered 2, log, 8. No shared rule was changed. Braille rules are tried in file order, so this one sits before the generic msub rules and takes precedence without touching them. Two measurements shaped it: the expression reaches the braille layer as a plain msub whose base is the function name - MathCAT's log-with-base intent rule does not apply on this path, so keying off the intent name would have been dead code narrowing the match so it can never fire fails exactly the two logarithm tests, which proves the rule really runs There is also a control test pinning an ORDINARY subscript, so the new rule cannot quietly swallow every index. Polish braille tests: 54 -> 57. Full braille suite 2039 passed, 0 failed.
…cross Reading the braille glyphs of the whole guide gives the group membership the spacing rules need (p. 3, 58-59): sign n blank left blank right groups equals 380 0.79 0.07 B, C' plus 254 0.88 0.09 B, C' minus 254 0.74 0.11 B, C' Three filters had to be found first, each by inspecting contexts: the FILLER sign stands in for a required blank where none can be written (p. 21, 26); a minus after a bracket, fraction opener or relation is unary, so the blank belongs to the sign before it; and cells are shared between signs - 64 of our 214 entries share one - so digits and letters must be excluded. Measuring CELLS instead of signs cannot work, which the first attempt showed: every cell came out unclassified because one cell averages signs with opposite properties. Multiplication and division stay out of that table - their cells are dominated in this document by the ordinal full stop (p. 4) and the date separator (p. 9). For them one worked example decides: p. 10 writes 67:14 with a blank before the division sign. Both now have tests, so the operator list is verified rather than assumed. Conflict resolution between groups is still not implemented; the measured table is recorded in definitions.yaml so it need not be redone. Polish braille tests: 57 -> 59. Full braille suite 2041 passed, 0 failed.
Conflict rule 1 (p. 59) says two signs are written with no blank when the first is from group C' - the guide's example of that group being '>'. Measuring the guide's glyphs against our output shows the rule already holds: x > 0 ours key-sign x, blank, >, number-sign 0 guide: the same, 4 times 7 > -1 ours blank before the relation, none after guide: the same The reason is structural: since the spacing rule emits the blank on the LEFT only (p. 58, group B), nothing can put one after a sign. A rule keyed on "the previous sign is group C'" would therefore be dead code, so these are tests instead. One of them records a wrong assumption worth keeping: a CLOSING BRACKET is not in group C'. It looks like it should be, but the guide writes (14 - 5) + 7 with a blank between the bracket and the plus. No rules changed in this commit. Polish braille tests: 59 -> 62.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Polish as an eleventh braille code, next to Nemeth, UEB, CMU, Vietnam,
Russian, Swedish, French, Finnish, LaTeX and ASCIIMath.
The code follows Brajlowska notacja matematyczna, fizyczna, chemiczna (2nd ed.,
Krakow-Laski-Lodz 2011), the notation recommended by the Polish Ministry of
Education for teaching blind pupils. It descends from the Epheser/Marburg
tradition, so some signs will look familiar next to the German code, but the
spacing rules and several key signs differ.
How the cell values were obtained
The guide is a PDF that renders braille with a subsetted font, so the text layer
gives the font's own byte values rather than braille. Every cell in this PR was
therefore read from the GLYPHS: the dot pattern was recovered from each glyph's
bounding box and mapped to the Unicode braille block, and the mapping was then
cross-checked against two codes already in the repo (letters and digits agree with
Swedish, the number sign with UEB).
Each entry and rule carries the guide's page number in a comment, so anything here
can be traced back to its source.
What is covered
rather than a capital sign stacked on a Greek one (p. 7)
indices with lowered digits (p. 33)
logarithms and the base written as an upper left index (pp. 36, 47, 52-56)
62 tests in
tests/braille/Polish/polish.rs, run as part of the braille suite.What is NOT covered, on purpose
bond diagrams are two-dimensional layouts, and MathCAT translates MathML
linearly. Out of scope rather than half-done.
sign twice - once for its left side (A/B/C) and once for its right (A'/B'/C') -
and gives an order for conflicts. Implemented: the group-B blank on the left of
relations and operators, and rule 1 for group C', which holds structurally
because the blank is only ever emitted on the left. The general pairwise
resolution is not implemented; the measured group membership for the signs we
could establish is recorded in
definitions.yamlso it need not be redone.run, but p. 57 states the general rule - the sign is repeated for every letter -
and the guide's own PLN, geometry points and vectors follow it. Handling this
needs Roman numerals to be RECOGNISED as such; collapsing every run of capitals
breaks ordinary uses, so the limitation is documented in the test file instead.
get_braille_charsreuses the UEB implementation, as Swedish does. The Polishletter and number key signs are applied in
polish_cleanup.Notes for review
src/braille.rsgains only the registration (theBrailleCodeimpl, the cleanupfunction and the indicator table) - the diff against main has no deleted lines
anywhere in the tree.
polish_cleanupcollapses the capital-plus-letter and Greek indicator pairs beforethe indicator table runs, because the Polish code has ONE sign for an upper-case
letter rather than a capital sign added to a lower-case one.
Spacing was the hardest part to get right, and the first version was wrong: blanks
were emitted symmetrically on both sides of every operator. Reading the guide's
glyphs over the whole document shows the asymmetry - for the equals sign, a blank
on the left in 79% of 380 occurrences and on the right in 3%; for plus, 88% and 9%;
for minus, 74% and 9%. Counting what follows a relation explains why: usually a
number sign or a letter sign, which is exactly the group C behaviour of rule 1.