Skip to content

Add Polish math braille code - #764

Open
michaldziwisz wants to merge 11 commits into
daisy:mainfrom
michaldziwisz:pl-braille-code
Open

Add Polish math braille code#764
michaldziwisz wants to merge 11 commits into
daisy:mainfrom
michaldziwisz:pl-braille-code

Conversation

@michaldziwisz

Copy link
Copy Markdown
Contributor

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

  • alphabet including the Polish diacritics, and digits
  • 48 Greek letters, with the guide's single lower-case and upper-case Greek signs
    rather than a capital sign stacked on a Greek one (p. 7)
  • operators, relations, negated relations, set theory, logic, geometry
  • fractions (short and full form), roots including graded roots, powers and
    indices with lowered digits (p. 33)
  • function names as contractions, trigonometry with the arcus form, limits,
    logarithms and the base written as an upper left index (pp. 36, 47, 52-56)
  • absolute value, divisibility, brackets in four pairs, punctuation after numbers
  • vectors, matrices and determinants, the projector technique
  • units of measurement via the unit sign (pp. 69-72)
  • non-structural chemistry: element symbols, molecules, ions (pp. 77-79)

62 tests in tests/braille/Polish/polish.rs, run as part of the braille suite.

What is NOT covered, on purpose

  • Structural chemistry (pp. 92-101, about 17% of the guide): ring compounds and
    bond diagrams are two-dimensional layouts, and MathCAT translates MathML
    linearly. Out of scope rather than half-done.
  • Full six-group spacing precedence (pp. 3, 58-59). The guide classifies every
    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.yaml so it need not be redone.
  • Roman numerals. Page 6 writes them with a single capital sign for the whole
    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_chars reuses the UEB implementation, as Swedish does. The Polish
    letter and number key signs are applied in polish_cleanup.

Notes for review

src/braille.rs gains only the registration (the BrailleCode impl, the cleanup
function and the indicator table) - the diff against main has no deleted lines
anywhere in the tree.

polish_cleanup collapses the capital-plus-letter and Greek indicator pairs before
the 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.

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.
@moritz-gross moritz-gross added rules Pertains to Rules translation Language translation of math/code braille Translation to Braille specifically labels Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

braille Translation to Braille specifically rules Pertains to Rules translation Language translation of math/code

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

2 participants