latexmath 0.1.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/LICENSE.md +33 -0
  4. data/README.adoc +133 -14
  5. data/artifacts/latex.bindings.python.json +1 -0
  6. data/artifacts/latex.bindings.rs.json +1 -0
  7. data/artifacts/latex.bindings.ruby.json +1 -0
  8. data/artifacts/latex.bindings.ts.json +1 -0
  9. data/artifacts/latex.checksums.txt +19 -0
  10. data/artifacts/latex.json +1 -0
  11. data/data/latex-symbols.json +3301 -0
  12. data/docs/model-contract.adoc +125 -0
  13. data/docs/parser-contract.adoc +349 -0
  14. data/docs/testsuite-pin.adoc +76 -0
  15. data/grammars/latex.wrappers.json +17 -0
  16. data/lib/latexmath/model/nodes.rb +918 -0
  17. data/lib/latexmath/model.rb +44 -0
  18. data/lib/latexmath/parser/artifact.rb +80 -0
  19. data/lib/latexmath/parser/configuration.rb +35 -0
  20. data/lib/latexmath/parser/engine.rb +161 -0
  21. data/lib/latexmath/parser/errors.rb +51 -0
  22. data/lib/latexmath/parser/native_recursion_guard.rb +43 -0
  23. data/lib/latexmath/parser/preprocessor.rb +77 -0
  24. data/lib/latexmath/parser/raw_parser.rb +84 -0
  25. data/lib/latexmath/parser/source_map.rb +121 -0
  26. data/lib/latexmath/parser/unwrap.rb +91 -0
  27. data/lib/latexmath/parser.rb +97 -0
  28. data/lib/latexmath/symbols.rb +74 -0
  29. data/lib/latexmath/version.rb +1 -1
  30. data/lib/latexmath.rb +5 -87
  31. data/model/latex.lml.checksums.txt +36 -0
  32. data/model/latex.lml.json +3350 -0
  33. metadata +60 -71
  34. data/.editorconfig +0 -14
  35. data/.github/workflows/test.yml +0 -80
  36. data/.gitignore +0 -22
  37. data/.rspec +0 -3
  38. data/.rubocop.yml +0 -129
  39. data/CODE_OF_CONDUCT.md +0 -74
  40. data/Gemfile +0 -10
  41. data/Gemfile.lock +0 -79
  42. data/Rakefile +0 -95
  43. data/bin/console +0 -14
  44. data/bin/setup +0 -8
  45. data/exe/latexmath +0 -8
  46. data/latexmath.gemspec +0 -34
  47. data/lib/latexmath/aggregator.rb +0 -351
  48. data/lib/latexmath/converter.rb +0 -424
  49. data/lib/latexmath/equation.rb +0 -13
  50. data/lib/latexmath/ext.rb +0 -9
  51. data/lib/latexmath/symbol.rb +0 -21
  52. data/lib/latexmath/tokenizer.rb +0 -82
  53. data/lib/latexmath/xml/builder.rb +0 -4
  54. data/lib/latexmath/xml/element.rb +0 -25
  55. data/lib/unimathsymbols.js.erb +0 -4
  56. data/lib/unimathsymbols.txt +0 -2864
  57. data/opal/latexmath-opal.rb +0 -40
  58. data/opal/ox.rb +0 -64
  59. data/opal/pseudoenumerator.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e751a24bfc7b92e3df8e1af211b838f679acaa888236d34b12853f43614891e
4
- data.tar.gz: 10f0770286d862f60da92690bc316a6f1926b1e11c95fe8f4249fe3a96a6e5bf
3
+ metadata.gz: a635c719d5f6d75f506de0542d17fba696d501cd9d1a80cf55e72477a6034a71
4
+ data.tar.gz: 2c7320734429e687c36964b3fb5a82aa75377ca82c594b565ff25c50b68d3e9d
5
5
  SHA512:
6
- metadata.gz: 2e3ff4b0ca14dad864a356f98e4d9f805fcdc182b58359396963724f62d9f9da433249c0f2471b36503189fdf351fab4693ee916fb82e708a675568a0267cce9
7
- data.tar.gz: e8b4bfd444e139005535f183769a8721a8b7bfea8ffde74c14028f4bd79398c0684dc0f2b515c353127eb8a4fd6df61b969e33d8c41b41451fe4abea56b85fab
6
+ metadata.gz: b401aa154dc56405a7aac319b044ec78e138a854421d993b368688a852cf7e1d60f0cef034bd98160ea7d5843d5284e15ca7527c436ed4b3c97a060c943e56ff
7
+ data.tar.gz: f42060ac13fce70b34ffa47e59ad6ed3a53660ff9cde08c2209d2be7b6f23c65dc506e11bd4b095bff4f64cb4d01509ea0407a03c7413853d632adf0b3072f33
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ Latexmath is now a LaTeX math parser that returns a typed model, built for
6
+ Plurimath. It replaces the 0.1.x LaTeX-to-MathML converter, and is not
7
+ backward compatible with it.
8
+
9
+ ### Breaking changes
10
+
11
+ - `Latexmath.parse(input, decimal: ".", engine: :auto)` returns a
12
+ `Latexmath::Model::Document`, a typed model of the formula built with
13
+ LutaML Model. In 0.1.x it returned an equation object whose `to_mathml`
14
+ wrote MathML.
15
+ - The 0.1.x converter is removed: `Latexmath.legacy_parse`, the
16
+ `Latexmath::Tokenizer`, `Aggregator`, `Converter`, `Equation`, `Symbol`
17
+ and `XML` classes, `Latexmath::Constants::SYMBOLS`, the `latexmath`
18
+ executable and the Opal build (`dist/latexmath.js`).
19
+ - Invalid input raises `Latexmath::ParseError` with the position in the
20
+ caller's input (`char_offset`, `byte_offset`). Every error Latexmath
21
+ raises on purpose is a `Latexmath::Error`.
22
+ - Ruby 3.2 or later is required. The runtime dependencies are Parsanol,
23
+ LutaML Model and HTMLEntities; Ox is no longer used. LaTeXML is no longer
24
+ needed, even for development.
25
+
26
+ ### Moving from 0.1.x
27
+
28
+ Latexmath no longer writes MathML. To convert LaTeX math to MathML or
29
+ another format, use Plurimath:
30
+
31
+ ```ruby
32
+ require "plurimath"
33
+
34
+ Plurimath::Math.parse("x^2", :latex).to_mathml
35
+ ```
36
+
37
+ Code that only needs the structure of a formula can read the model from
38
+ `Latexmath.parse` directly; see `docs/model-contract.adoc`. To keep the
39
+ 0.1.x converter, pin `gem "latexmath", "~> 0.1.5"`.
40
+
41
+ ### Other changes
42
+
43
+ - The grammar is Plurimath's LaTeX grammar, compiled with Parsanol into a
44
+ checksummed artifact (`artifacts/latex.json`). It runs on Parsanol's
45
+ native engine where available and on its Ruby engine elsewhere, with the
46
+ same results.
47
+ - CI replays the LaTeX generation of plurimath-testsuite pinned in
48
+ `spec/fixtures/testsuite-pin.yml` and compares the parse tree with the
49
+ pinned Plurimath release for every LaTeX case of the testsuite.
50
+ - The gem is released by trusted publishing from the `release` workflow.
data/LICENSE.md ADDED
@@ -0,0 +1,33 @@
1
+ Licenses & Copyright
2
+ ====================
3
+
4
+ This license file adheres to the formatting guidelines of
5
+ [readable-licenses](https://github.com/nevir/readable-licenses).
6
+
7
+
8
+ Ribose's BSD 2-Clause License
9
+ -----------------------------
10
+
11
+ Copyright (c) 2026, [Ribose Inc](https://www.ribose.com).
12
+ All rights reserved.
13
+
14
+ Redistribution and use in source and binary forms, with or without modification,
15
+ are permitted provided that the following conditions are met:
16
+
17
+ 1. Redistributions of source code must retain the above copyright notice,
18
+ this list of conditions and the following disclaimer.
19
+
20
+ 2. Redistributions in binary form must reproduce the above copyright notice,
21
+ this list of conditions and the following disclaimer in the documentation
22
+ and/or other materials provided with the distribution.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.adoc CHANGED
@@ -1,28 +1,147 @@
1
- = latexmath
1
+ = Latexmath: LaTeX math parser for Ruby
2
2
 
3
3
  image:https://img.shields.io/gem/v/latexmath.svg["Gem Version", link="https://rubygems.org/gems/latexmath"]
4
- image:https://github.com/metanorma/latexmath/workflows/test/badge.svg["Build Status", link="https://github.com/metanorma/latexmath/actions?workflow=test"]
5
- image:https://codeclimate.com/github/metanorma/latexmath/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/metanorma/latexmath"]
6
- image:https://img.shields.io/github/issues-pr-raw/metanorma/latexmath.svg["Pull Requests", link="https://github.com/metanorma/latexmath/pulls"]
7
- image:https://img.shields.io/github/commits-since/metanorma/latexmath/latest.svg["Commits since latest",link="https://github.com/metanorma/latexmath/releases"]
4
+ image:https://github.com/plurimath/latexmath/actions/workflows/test.yml/badge.svg["Build Status", link="https://github.com/plurimath/latexmath/actions/workflows/test.yml"]
8
5
 
9
6
  == Purpose
10
7
 
11
- Ruby gem to translate LaTeX math into MathML.
12
- The MathML is supposed to be compliant with output of LaTeXML.
8
+ Latexmath parses LaTeX math, the notation inside `$...$` or a `\[...\]`
9
+ display, into a typed model of the formula. It reads the LaTeX that
10
+ https://github.com/plurimath/plurimath[Plurimath] reads, with the same
11
+ grammar, and is built for Plurimath to use for LaTeX input
12
+ (https://github.com/plurimath/plurimath/issues/531[plurimath/plurimath#531]).
13
13
 
14
- It also works in reverse by translating MathML into LaTeX code.
14
+ The model records the structure of the formula: which symbols, fractions,
15
+ roots, sub- and superscripts, matrices, environments and brackets it
16
+ contains, and how they nest. Your code decides what to do with it, such as
17
+ rendering, converting or analysing it. Latexmath does not write MathML or any
18
+ other output format; Plurimath does that.
15
19
 
16
- The LaTeX math syntax supports `amsmath`, `array`, `split` and `pmatrix` packages.
20
+ Version 1.0.0 replaces the 0.1.x gem, which converted LaTeX to MathML. See
21
+ link:CHANGELOG.md[the changelog] for what changed and how to move.
17
22
 
18
- NOTE: Bolding, symbols like `\backepsilon`, equation alignment, are all supported.
23
+ == Installation
24
+
25
+ Add it to your `Gemfile`:
26
+
27
+ [source,ruby]
28
+ ----
29
+ gem "latexmath", "~> 1.0"
30
+ ----
31
+
32
+ Latexmath needs Ruby 3.2 or later. Its runtime dependencies are
33
+ https://github.com/parsanol/parsanol-ruby[Parsanol],
34
+ https://github.com/lutaml/lutaml-model[LutaML Model] and HTMLEntities. It
35
+ does not load Plurimath.
19
36
 
20
37
  == Usage
21
38
 
22
39
  [source,ruby]
23
- -------------
40
+ ----
24
41
  require "latexmath"
25
42
 
26
- latex = "$$f_i = \sum_{j=1}^2 s_{ij} n_j \quad {\rm for} \quad i = 1,2$$"
27
- mathml = Latexmath.parse(latex).to_mathml
28
- -------------
43
+ doc = Latexmath.parse("x^2")
44
+ power = doc.expression.power
45
+ power.symbols.spelling # => "x"
46
+ power.supscript.number # => "2"
47
+ ----
48
+
49
+ `Latexmath.parse` returns a `Latexmath::Model::Document`. Its `expression`
50
+ holds the formula and `decimal` the decimal marker it was parsed with. Input
51
+ with no formula, such as an empty string, gives a document whose `expression`
52
+ is nil.
53
+
54
+ The model classes are plain LutaML Model classes, generated from the grammar:
55
+ each named part of the grammar is a class with one attribute per part it can
56
+ contain. Symbols such as `\alpha` are `Latexmath::Model::MathSymbol` objects.
57
+ A symbol keeps the text the grammar matched as `spelling` and gives its
58
+ Unicode text and kind (`symbol`, `open` or `close`):
59
+
60
+ [source,ruby]
61
+ ----
62
+ symbol = Latexmath.parse("\\alpha").expression.symbols
63
+ symbol.spelling # => "alpha"
64
+ symbol.value # => "α"
65
+ symbol.kind # => "symbol"
66
+ ----
67
+
68
+ Numbers keep their digits and decimal marker as written. A dot is the
69
+ decimal marker unless you say otherwise; a comma and the Arabic decimal
70
+ separator are also supported:
71
+
72
+ [source,ruby]
73
+ ----
74
+ Latexmath.parse("1,5", decimal: ",")
75
+ Latexmath.parse("1٫5", decimal: "٫")
76
+ ----
77
+
78
+ The rules for each model class are in link:docs/model-contract.adoc[the
79
+ model contract], and the accepted syntax and the preprocessing of the input
80
+ in link:docs/parser-contract.adoc[the parser contract].
81
+
82
+ == Errors
83
+
84
+ Input that is not valid LaTeX math raises `Latexmath::ParseError`, which
85
+ tells you where the parser gave up, as a position in the text you passed:
86
+
87
+ [source,ruby]
88
+ ----
89
+ begin
90
+ Latexmath.parse("é}")
91
+ rescue Latexmath::ParseError => e
92
+ e.char_offset # => 1, position in characters
93
+ e.byte_offset # => 2, position in UTF-8 bytes
94
+ end
95
+ ----
96
+
97
+ The parser first rewrites the input as Plurimath does: it removes spaces and
98
+ newlines and writes non-ASCII characters as HTML entities. The place the
99
+ grammar stops is therefore a position in text you did not write. The offsets are mapped back to your input; a position
100
+ inside rewritten text points at the start of the text it came from. The
101
+ position in the rewritten text is also available, as `grammar_char_offset`
102
+ and `grammar_byte_offset`.
103
+
104
+ Every error Latexmath raises on purpose is a `Latexmath::Error`, except
105
+ `ArgumentError` for an invalid argument such as an unknown engine or decimal
106
+ marker.
107
+
108
+ == Engines
109
+
110
+ Latexmath runs on Parsanol's native engine where its extension is available,
111
+ and on its pure Ruby engine elsewhere. Both give the same results. You can
112
+ choose one with `engine: :native` or `engine: :ruby`. Asking for the native
113
+ engine where it cannot run raises `Latexmath::EngineUnavailable` instead of
114
+ quietly using Ruby. `Latexmath.last_engine` tells you which engine handled
115
+ the last call on the current thread.
116
+
117
+ The native engine has a recursion limit. With the default `engine: :auto`,
118
+ input that nests deeper than that is parsed with the Ruby engine; with
119
+ `engine: :native` it raises `Latexmath::NestingTooDeep`, a kind of
120
+ `Latexmath::ParseError` that has no offsets, because the input may well be
121
+ valid.
122
+
123
+ == Testing against the Plurimath testsuite
124
+
125
+ The LaTeX cases of
126
+ https://github.com/plurimath/plurimath-testsuite[plurimath-testsuite] are
127
+ kept there as numbered, immutable generations. `spec/fixtures/testsuite-pin.yml`
128
+ names the exact commit, generation and digest this gem is tested against, and
129
+ CI replays every raw-tree row of that generation on both engines. CI also
130
+ compares the parse tree with the one the pinned Plurimath release produces
131
+ for every LaTeX case of the testsuite. See
132
+ link:docs/testsuite-pin.adoc[the testsuite pin].
133
+
134
+ == Relation to Plurimath
135
+
136
+ Latexmath is one of the language gems split out of Plurimath, with
137
+ https://github.com/plurimath/amath[amath] for AsciiMath and
138
+ https://github.com/plurimath/unicodemath[unicodemath] for UnicodeMath. The
139
+ plan and its progress are tracked in
140
+ https://github.com/plurimath/plurimath/issues/541[plurimath/plurimath#541].
141
+ Plurimath is to turn the model into its own formula objects and write the
142
+ output formats.
143
+
144
+ == License
145
+
146
+ Copyright Ribose Inc. Licensed under the BSD 2-Clause License; see
147
+ link:LICENSE.md[].
@@ -0,0 +1 @@
1
+ {"artifact_kind":"parg-bindings","target":"python","binding_version":1,"grammar":"Latex","grammar_version":"0.1.0","grammar_source_hash":"f0694ce57b0f3e99e536b3749eb6b9c56570e4311b1d7aca0047c9e25a668cbf","shape":"parsanol-tree/v2","entries":{"dot":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"comma":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"arabic":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]}},"checksum":"sha256:74c9d54e22cb43fb1667bab1a6f1c3fecf177cf998756902355bd946f4142380"}
@@ -0,0 +1 @@
1
+ {"artifact_kind":"parg-bindings","target":"rs","binding_version":1,"grammar":"Latex","grammar_version":"0.1.0","grammar_source_hash":"f0694ce57b0f3e99e536b3749eb6b9c56570e4311b1d7aca0047c9e25a668cbf","shape":"parsanol-tree/v2","entries":{"dot":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"comma":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"arabic":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]}},"checksum":"sha256:7140c6478d723a74e8363ed1fb64619072cb94e1fee27ae999d4095d0094bacc"}
@@ -0,0 +1 @@
1
+ {"artifact_kind":"parg-bindings","target":"ruby","binding_version":1,"grammar":"Latex","grammar_version":"0.1.0","grammar_source_hash":"f0694ce57b0f3e99e536b3749eb6b9c56570e4311b1d7aca0047c9e25a668cbf","shape":"parsanol-tree/v2","entries":{"dot":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"comma":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"arabic":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]}},"checksum":"sha256:35d47a16342a91651ca9be16bb0436f02c8b4528afd0a3757b415366f29a6b3f"}
@@ -0,0 +1 @@
1
+ {"artifact_kind":"parg-bindings","target":"ts","binding_version":1,"grammar":"Latex","grammar_version":"0.1.0","grammar_source_hash":"f0694ce57b0f3e99e536b3749eb6b9c56570e4311b1d7aca0047c9e25a668cbf","shape":"parsanol-tree/v2","entries":{"dot":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"comma":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]},"arabic":{"bindings":[{"capture":"latex_tree","path":"latex_tree","type":"raw","card":null,"preprocess":null},{"capture":"latex_tree","path":"model","type":"any","card":null,"preprocess":null}]}},"checksum":"sha256:ecff4bfe9a7c8eed7b9df7ca833ecec5ff6425b837278de96a88374b4d654b78"}
@@ -0,0 +1,19 @@
1
+ # Written by scripts/compile.rb. Envelope checksums are Parsanol's own;
2
+ # the sha256 lines are of the files' bytes.
3
+ artifact_checksum sha256:5639a6ca46058450f500d340649c95510b2a880ce0e4ce1a8d50e95eabad465f
4
+ compiler_version 1.3.132
5
+ edition 2
6
+ source_hash sha256:f0694ce57b0f3e99e536b3749eb6b9c56570e4311b1d7aca0047c9e25a668cbf
7
+ artifacts/latex.json sha256:f1f2e5d8328be8686b2c85907d23327a5081de0fa765c1b04b5de2a20d96f84b
8
+ bindings ruby checksum sha256:35d47a16342a91651ca9be16bb0436f02c8b4528afd0a3757b415366f29a6b3f
9
+ artifacts/latex.bindings.ruby.json sha256:6aec2810d9d52ea12bd1f65a8bc06c3074a866257f6a0fdd10948a8927f3720b
10
+ bindings rs checksum sha256:7140c6478d723a74e8363ed1fb64619072cb94e1fee27ae999d4095d0094bacc
11
+ artifacts/latex.bindings.rs.json sha256:55c9e24177b459a313b8990bba111b7769875a02cc9476909f1383da66e3bcc5
12
+ bindings ts checksum sha256:ecff4bfe9a7c8eed7b9df7ca833ecec5ff6425b837278de96a88374b4d654b78
13
+ artifacts/latex.bindings.ts.json sha256:94184309e07268d41cafae75b700ebad7f61d16ef83f3aac4b0f775961916441
14
+ bindings python checksum sha256:74c9d54e22cb43fb1667bab1a6f1c3fecf177cf998756902355bd946f4142380
15
+ artifacts/latex.bindings.python.json sha256:cbdff4ef98335a187bd4a1d3dda2d34af61cfb5d3d0b6c411385ccb3987f0ef8
16
+ grammars/latex.parg sha256:f0694ce57b0f3e99e536b3749eb6b9c56570e4311b1d7aca0047c9e25a668cbf
17
+ data/latex-atoms.json sha256:405440c7f5e90a866e5b36d8145e547572c8c67779984726bcc52c38972950c9
18
+ entries dot comma arabic
19
+ order_warnings 31906