plurimath 0.2.0 → 0.2.1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +8 -11
  3. data/.gitignore +1 -0
  4. data/AsciiMath-Supported-Data.adoc +280 -0
  5. data/Gemfile +1 -0
  6. data/Latex-Supported-Data.adoc +1872 -0
  7. data/MathML-Supported-Data.adoc +270 -0
  8. data/README.adoc +94 -0
  9. data/lib/plurimath/asciimath/constants.rb +215 -222
  10. data/lib/plurimath/asciimath/parse.rb +45 -11
  11. data/lib/plurimath/asciimath/parser.rb +4 -3
  12. data/lib/plurimath/asciimath/transform.rb +222 -131
  13. data/lib/plurimath/asciimath.rb +1 -1
  14. data/lib/plurimath/html/constants.rb +50 -0
  15. data/lib/plurimath/html/parse.rb +149 -0
  16. data/lib/plurimath/html/parser.rb +26 -0
  17. data/lib/plurimath/html/transform.rb +363 -0
  18. data/lib/plurimath/html.rb +1 -1
  19. data/lib/plurimath/latex/constants.rb +1885 -1858
  20. data/lib/plurimath/latex/parse.rb +127 -34
  21. data/lib/plurimath/latex/parser.rb +5 -4
  22. data/lib/plurimath/latex/transform.rb +324 -164
  23. data/lib/plurimath/math/formula.rb +9 -1
  24. data/lib/plurimath/math/function/bar.rb +1 -1
  25. data/lib/plurimath/math/function/base.rb +7 -1
  26. data/lib/plurimath/math/function/binary_function.rb +10 -5
  27. data/lib/plurimath/math/function/color.rb +4 -4
  28. data/lib/plurimath/math/function/fenced.rb +7 -2
  29. data/lib/plurimath/math/function/font_style/bold.rb +18 -0
  30. data/lib/plurimath/math/function/font_style/double_struck.rb +18 -0
  31. data/lib/plurimath/math/function/font_style/fraktur.rb +18 -0
  32. data/lib/plurimath/math/function/font_style/monospace.rb +18 -0
  33. data/lib/plurimath/math/function/font_style/sans-serif.rb +18 -0
  34. data/lib/plurimath/math/function/font_style/script.rb +18 -0
  35. data/lib/plurimath/math/function/font_style.rb +2 -22
  36. data/lib/plurimath/math/function/frac.rb +4 -4
  37. data/lib/plurimath/math/function/inf.rb +0 -1
  38. data/lib/plurimath/math/function/left.rb +3 -6
  39. data/lib/plurimath/math/function/limits.rb +0 -1
  40. data/lib/plurimath/math/function/log.rb +6 -0
  41. data/lib/plurimath/math/function/mod.rb +6 -0
  42. data/lib/plurimath/math/function/multiscript.rb +11 -0
  43. data/lib/plurimath/math/function/norm.rb +2 -1
  44. data/lib/plurimath/math/function/over.rb +29 -0
  45. data/lib/plurimath/math/function/overset.rb +2 -2
  46. data/lib/plurimath/math/function/power.rb +7 -1
  47. data/lib/plurimath/math/function/power_base.rb +14 -7
  48. data/lib/plurimath/math/function/prod.rb +6 -0
  49. data/lib/plurimath/math/function/right.rb +24 -0
  50. data/lib/plurimath/math/function/root.rb +5 -4
  51. data/lib/plurimath/math/function/sqrt.rb +1 -1
  52. data/lib/plurimath/math/function/substack.rb +0 -1
  53. data/lib/plurimath/math/function/sum.rb +6 -0
  54. data/lib/plurimath/math/function/table/align.rb +24 -0
  55. data/lib/plurimath/math/function/table/array.rb +25 -0
  56. data/lib/plurimath/math/function/table/bmatrix.rb +26 -0
  57. data/lib/plurimath/math/function/table/matrix.rb +24 -0
  58. data/lib/plurimath/math/function/table/multline.rb +24 -0
  59. data/lib/plurimath/math/function/table/pmatrix.rb +24 -0
  60. data/lib/plurimath/math/function/table/split.rb +24 -0
  61. data/lib/plurimath/math/function/table/vmatrix.rb +25 -0
  62. data/lib/plurimath/math/function/table.rb +17 -5
  63. data/lib/plurimath/math/function/td.rb +6 -1
  64. data/lib/plurimath/math/function/ternary_function.rb +16 -6
  65. data/lib/plurimath/math/function/text.rb +19 -7
  66. data/lib/plurimath/math/function/tr.rb +6 -1
  67. data/lib/plurimath/math/function/unary_function.rb +5 -0
  68. data/lib/plurimath/math/function/vec.rb +4 -0
  69. data/lib/plurimath/math/function.rb +13 -2
  70. data/lib/plurimath/math/number.rb +8 -0
  71. data/lib/plurimath/math/symbol.rb +12 -3
  72. data/lib/plurimath/math.rb +9 -4
  73. data/lib/plurimath/mathml/constants.rb +2 -34
  74. data/lib/plurimath/mathml/parse.rb +7 -2
  75. data/lib/plurimath/mathml/parser.rb +2 -1
  76. data/lib/plurimath/mathml/transform.rb +73 -68
  77. data/lib/plurimath/mathml.rb +1 -1
  78. data/lib/plurimath/omml/constants.rb +154 -0
  79. data/lib/plurimath/omml/parser.rb +22 -0
  80. data/lib/plurimath/omml/transform.rb +216 -0
  81. data/lib/plurimath/omml.rb +1 -1
  82. data/lib/plurimath/unitsml.rb +4 -0
  83. data/lib/plurimath/utility.rb +73 -0
  84. data/lib/plurimath/version.rb +1 -1
  85. data/plurimath.gemspec +1 -0
  86. metadata +49 -7
  87. data/README.md +0 -40
@@ -3,233 +3,226 @@
3
3
  module Plurimath
4
4
  class Asciimath
5
5
  class Constants
6
- PARENTHESIS = {
6
+ TABLE_PARENTHESIS = {
7
+ "[": "]",
8
+ "{": "}",
7
9
  "(": ")",
10
+ "|": "|",
11
+ }.freeze
12
+ PARENTHESIS = {
8
13
  "(:": ":)",
9
- "{": "}",
10
14
  "{:": ":}",
15
+ "(": ")",
16
+ "{": "}",
11
17
  "[": "]",
12
18
  }.freeze
13
- SYMBOLS = %i[
14
- twoheadrightarrowtail
15
- twoheadrightarrow
16
- rightarrowtail
17
- Leftrightarrow
18
- leftrightarrow
19
- varepsilon
20
- Rightarrow
21
- rightarrow
22
- Leftarrow
23
- leftarrow
24
- backslash
25
- downarrow
26
- therefore
27
- triangle
28
- vartheta
29
- bigwedge
30
- rceiling
31
- lceiling
32
- setminus
33
- supseteq
34
- subseteq
35
- emptyset
36
- "|\\ |"
37
- upsilon
38
- diamond
39
- uparrow
40
- implies
41
- partial
42
- epsilon
43
- because
44
- |cdots|
45
- |ldots|
46
- bigcap
47
- bigvee
48
- propto
49
- approx
50
- exists
51
- forall
52
- otimes
53
- ltimes
54
- bowtie
55
- rtimes
56
- models
57
- mapsto
58
- bigcup
59
- lambda
60
- Lambda
61
- "(x:}"
62
- "{:x)"
63
- succeq
64
- preceq
65
- varphi
66
- rfloor
67
- lfloor
68
- square
69
- |quad|
70
- supset
71
- subset
72
- vdash
73
- times
74
- wedge
75
- oplus
76
- alpha
77
- nabla
78
- delta
79
- theta
80
- gamma
81
- Gamma
82
- Delta
83
- Theta
84
- kappa
85
- ddots
86
- vdots
87
- |...|
88
- equiv
89
- Sigma
90
- Omega
91
- omega
92
- aleph
93
- infty
94
- sigma
95
- frown
96
- notin
97
- angle
98
- succ
99
- prec
100
- cong
101
- \\\\
102
- star
103
- odot
104
- cdot
105
- rarr
106
- darr
107
- prop
108
- lArr
109
- rArr
110
- uarr
111
- beta
112
- hArr
113
- harr
114
- larr
115
- grad
116
- circ
117
- >->>
118
- zeta
119
- iota
120
- /_\\
121
- sube
122
- sup
123
- sub
124
- !in
125
- eta
126
- top
127
- |><|
128
- __|
129
- |__
130
- ":'"
131
- Phi
132
- supe
133
- Psi
134
- psi
135
- chi
136
- >-=
137
- -<=
138
- mgt
139
- mlt
140
- ><|
141
- |==
142
- |--
143
- vvv
144
- vee
145
- nnn
146
- cap
147
- ^^^
148
- div
149
- ast
150
- bot
151
- del
152
- and
153
- neg
154
- not
155
- uuu
156
- cup
157
- iff
158
- phi
159
- rho
160
- tau
161
- |->
162
- >->
163
- <=>
164
- ->>
165
- ^^
166
- nn
167
- vv
168
- TT
169
- EE
170
- ox
171
- xx
172
- o+
173
- o.
174
- |><
175
- _|_
176
- ***
177
- **
178
- -:
179
- ~~
180
- //
181
- pm
182
- O/
183
- ->
184
- =>
185
- +-
186
- to
187
- Xi
188
- AA
189
- if
190
- uu
191
- or
192
- lt
193
- ne
194
- nu
195
- mu
196
- ZZ
197
- RR
198
- QQ
199
- NN
200
- CC
201
- >>
202
- <<
203
- ~|
204
- !=
205
- >-
206
- -<
207
- ~=
208
- -=
209
- pi
210
- xi
211
- oo
212
- Pi
213
- :.
214
- gg
215
- ll
216
- ge
217
- >=
218
- le
219
- <=
220
- gt
221
- |~
222
- in
223
- /_
224
- >
225
- @
226
- /
227
- *
228
- -
229
- <
230
- =
231
- +
232
- ].freeze
19
+ SYMBOLS = {
20
+ twoheadrightarrowtail: :"&#x2916;",
21
+ twoheadrightarrow: :"&#x21A0;",
22
+ rightarrowtail: :"&#x21A3;",
23
+ Leftrightarrow: :"&#x21D4;",
24
+ leftrightarrow: :"&#x2194;",
25
+ Rightarrow: :"&#x21D2;",
26
+ rightarrow: :"&#x2192;",
27
+ varepsilon: :"&#x25B;",
28
+ Leftarrow: :"&#x21D0;",
29
+ leftarrow: :"&#x2190;",
30
+ downarrow: :"&#x2193;",
31
+ therefore: :"&#x2234;",
32
+ backslash: :"\\",
33
+ setminus: :"\\",
34
+ triangle: :"&#x25B3;",
35
+ bigwedge: :"&#x22C0;",
36
+ rceiling: :"&#x2309;",
37
+ lceiling: :"&#x2308;",
38
+ supseteq: :"&#x2287;",
39
+ subseteq: :"&#x2286;",
40
+ vartheta: :"&#x3D1;",
41
+ emptyset: :"&#x2205;",
42
+ diamond: :"&#x22C4;",
43
+ uparrow: :"&#x2191;",
44
+ implies: :"&#x21D2;",
45
+ partial: :"&#x2202;",
46
+ because: :"&#x2235;",
47
+ upsilon: :"&#x3C5;",
48
+ epsilon: :"&#x3B5;",
49
+ bigcap: :"&#x22C2;",
50
+ bigvee: :"&#x22C1;",
51
+ propto: :"&#x221D;",
52
+ approx: :"&#x2248;",
53
+ exists: :"&#x2203;",
54
+ forall: :"&#x2200;",
55
+ otimes: :"&#x2297;",
56
+ ltimes: :"&#x22C9;",
57
+ bowtie: :"&#x22C8;",
58
+ rtimes: :"&#x22CA;",
59
+ models: :"&#x22A8;",
60
+ mapsto: :"&#x21A6;",
61
+ bigcup: :"&#x22C3;",
62
+ succeq: :"&#x2AB0;",
63
+ preceq: :"&#x2AAF;",
64
+ rfloor: :"&#x230B;",
65
+ lfloor: :"&#x230A;",
66
+ square: :"&#x25A1;",
67
+ supset: :"&#x2283;",
68
+ subset: :"&#x2282;",
69
+ lambda: :"&#x3BB;",
70
+ Lambda: :"&#x39B;",
71
+ varphi: :"&#x3C6;",
72
+ ">->>": :"&#x2916;",
73
+ "/_\\": :"&#x25B3;",
74
+ "|><|": :"&#x22C8;",
75
+ kappa: :"&#x3BA;",
76
+ Delta: :"&#x394;",
77
+ delta: :"&#x3B4;",
78
+ gamma: :"&#x3B3;",
79
+ Gamma: :"&#x393;",
80
+ Theta: :"&#x398;",
81
+ theta: :"&#x3B8;",
82
+ alpha: :"&#x3B1;",
83
+ aleph: :"&#2135;",
84
+ infty: :"&#221E;",
85
+ equiv: :"&#2261;",
86
+ frown: :"&#2322;",
87
+ notin: :"&#2209;",
88
+ angle: :"&#2220;",
89
+ "!in": :"&#x2209;",
90
+ cdots: :"&#x22EF;",
91
+ vdash: :"&#x22A2;",
92
+ wedge: :"&#x2227;",
93
+ oplus: :"&#x2295;",
94
+ nabla: :"&#x2207;",
95
+ ddots: :"&#x22F1;",
96
+ vdots: :"&#x22EE;",
97
+ Sigma: :"&#3A3;",
98
+ Omega: :"&#3A9;",
99
+ omega: :"&#3C9;",
100
+ sigma: :"&#3C3;",
101
+ times: :"&#xD7;",
102
+ ldots: :"...",
103
+ ">-=": :"&#x2AB0;",
104
+ "-<=": :"&#x2AAF;",
105
+ "><|": :"&#x22CA;",
106
+ "|==": :"&#x22A8;",
107
+ "|--": :"&#x22A2;",
108
+ "^^^": :"&#x22C0;",
109
+ "|->": :"&#x21A6;",
110
+ ">->": :"&#x21A3;",
111
+ "->>": :"&#x21A0;",
112
+ "__|": :"&#x230B;",
113
+ "|__": :"&#x230A;",
114
+ "|><": :"&#x22C9;",
115
+ "_|_": :"&#x22A5;",
116
+ "***": :"&#x22C6;",
117
+ "<=>": :"&#x21D4;",
118
+ quad: :"&#xA0;&#xA0;",
119
+ star: :"&#x22C6;",
120
+ odot: :"&#x2299;",
121
+ cdot: :"&#x22C5;",
122
+ rarr: :"&#x2192;",
123
+ darr: :"&#x2193;",
124
+ prop: :"&#x221D;",
125
+ lArr: :"&#x21D0;",
126
+ rArr: :"&#x21D2;",
127
+ uarr: :"&#x2191;",
128
+ hArr: :"&#x21D4;",
129
+ harr: :"&#x2194;",
130
+ larr: :"&#x2190;",
131
+ grad: :"&#x2207;",
132
+ circ: :"&#x2218;",
133
+ sube: :"&#x2286;",
134
+ supe: :"&#x2287;",
135
+ succ: :"&#227B;",
136
+ prec: :"&#227A;",
137
+ cong: :"&#2245;",
138
+ beta: :"&#x3B2;",
139
+ zeta: :"&#x3B6;",
140
+ iota: :"&#x3B9;",
141
+ ":'": :"&#x2235;",
142
+ "^^": :"&#x2227;",
143
+ "o+": :"&#x2295;",
144
+ "o.": :"&#x2299;",
145
+ "**": :"&#x2217;",
146
+ "~~": :"&#x2248;",
147
+ "O/": :"&#x2205;",
148
+ "->": :"&#x2192;",
149
+ "=>": :"&#x21D2;",
150
+ ">>": :"&#x232A;",
151
+ "<<": :"&#x2329;",
152
+ "~|": :"&#x2309;",
153
+ "!=": :"&#x2260;",
154
+ ">-": :"&#x227B;",
155
+ "-<": :"&#x227A;",
156
+ "~=": :"&#x2245;",
157
+ "-=": :"&#x2261;",
158
+ ":.": :"&#x2234;",
159
+ ">=": :"&#x2265;",
160
+ "<=": :"&#x2264;",
161
+ "|~": :"&#x2308;",
162
+ "/_": :"&#x2220;",
163
+ "+-": :"&#xB1;",
164
+ "-:": :"&#xF7;",
165
+ sup: :"&#x2283;",
166
+ sub: :"&#x2282;",
167
+ top: :"&#x22A4;",
168
+ vvv: :"&#x22C1;",
169
+ vee: :"&#x2228;",
170
+ nnn: :"&#x22C2;",
171
+ cap: :"&#x2229;",
172
+ ast: :"&#x2217;",
173
+ bot: :"&#x22A5;",
174
+ del: :"&#x2202;",
175
+ uuu: :"&#x22C3;",
176
+ cup: :"&#x222A;",
177
+ iff: :"&#x21D4;",
178
+ eta: :"&#x3B7;",
179
+ Phi: :"&#x3A6;",
180
+ Psi: :"&#x3A8;",
181
+ psi: :"&#x3C8;",
182
+ chi: :"&#x3C7;",
183
+ phi: :"&#x3D5;",
184
+ rho: :"&#x3C1;",
185
+ tau: :"&#x3C4;",
186
+ div: :"&#xF7;",
187
+ neg: :"&#xAC;",
188
+ not: :"&#xAC;",
189
+ "@": :"&#x2218;",
190
+ "*": :"&#x22C5;",
191
+ "<": :"&lt;",
192
+ ">": :"&gt;",
193
+ "-": :"-",
194
+ "=": :"=",
195
+ "+": :"+",
196
+ "/": :"/",
197
+ nn: :"&#x2229;",
198
+ vv: :"&#x2228;",
199
+ TT: :"&#x22A4;",
200
+ EE: :"&#x2203;",
201
+ ox: :"&#x2297;",
202
+ to: :"&#x2192;",
203
+ AA: :"&#x2200;",
204
+ uu: :"&#x222A;",
205
+ ne: :"&#x2260;",
206
+ ZZ: :"&#x2124;",
207
+ RR: :"&#x211D;",
208
+ QQ: :"&#x211A;",
209
+ NN: :"&#x2115;",
210
+ CC: :"&#x2102;",
211
+ oo: :"&#x221E;",
212
+ ge: :"&#x2265;",
213
+ le: :"&#x2264;",
214
+ in: :"&#x2208;",
215
+ nu: :"&#x3BD;",
216
+ mu: :"&#x3BC;",
217
+ pi: :"&#x3C0;",
218
+ Pi: :"&#x3A0;",
219
+ xi: :"&#x3BE;",
220
+ Xi: :"&#x39E;",
221
+ xx: :"&#xD7;",
222
+ pm: :"&#xB1;",
223
+ gt: :"&gt;",
224
+ lt: :"&lt;",
225
+ }.freeze
233
226
  UNARY_CLASSES = %i[
234
227
  arccos
235
228
  arcsin
@@ -4,17 +4,32 @@ require "parslet"
4
4
  module Plurimath
5
5
  class Asciimath
6
6
  class Parse < Parslet::Parser
7
- rule(:base) { str("_").as(:_) }
7
+ rule(:space) { str(" ") }
8
+ rule(:base) { str("_").as(:_) }
9
+ rule(:power) { str("^").as(:^) }
10
+ rule(:comma) { (str(",") >> space) | str(",") }
8
11
 
9
- rule(:power) { str("^").as(:^) }
12
+ rule(:symbols) { arr_to_expression(Constants::SYMBOLS.keys, :symbol) }
13
+ rule(:font_style) { arr_to_expression(Constants::FONT_STYLES, :fonts) }
10
14
 
11
- rule(:symbols) { arr_to_expression(Constants::SYMBOLS, :symbol) }
15
+ rule(:unary_functions) { arr_to_expression(Constants::UNARY_CLASSES) }
16
+ rule(:binary_functions) { arr_to_expression(Constants::BINARY_CLASSES) }
12
17
 
13
- rule(:unary_functions) { arr_to_expression(Constants::UNARY_CLASSES) }
18
+ rule(:left_right_open_paren) { str("(") | str("[") }
19
+ rule(:left_right_close_paren) { str(")") | str("]") }
14
20
 
15
- rule(:font_style) { arr_to_expression(Constants::FONT_STYLES, :fonts) }
21
+ rule(:left_right) do
22
+ (str("left") >> left_right_open_paren.as(:left) >> iteration.as(:left_right_value) >> str("right") >> left_right_close_paren.as(:right)) |
23
+ table
24
+ end
16
25
 
17
- rule(:binary_functions) { arr_to_expression(Constants::BINARY_CLASSES) }
26
+ rule(:open_table) do
27
+ arr_to_expression(Constants::TABLE_PARENTHESIS.keys, :table_left)
28
+ end
29
+
30
+ rule(:close_table) do
31
+ arr_to_expression(Constants::TABLE_PARENTHESIS.values, :table_right)
32
+ end
18
33
 
19
34
  rule(:lparen) do
20
35
  Constants::PARENTHESIS.keys.reduce do |expression, parenthesis|
@@ -43,6 +58,21 @@ module Plurimath
43
58
  match("[0-9]").repeat(1).as(:number)
44
59
  end
45
60
 
61
+ rule(:table) do
62
+ (open_table.as(:table_left) >> tr >> close_table.as(:table_right)) |
63
+ (str("left") >> left_right_open_paren.as(:left) >> tr >> str("right") >> left_right_close_paren.as(:right))
64
+ end
65
+
66
+ rule(:tr) do
67
+ ((str("[").as(:open_tr) >> td.as(:tds_list) >> str("]")).as(:table_row) >> comma >> tr.as(:expr)) |
68
+ (str("[").as(:open_tr) >> td.as(:tds_list) >> str("]")).as(:table_row)
69
+ end
70
+
71
+ rule(:td) do
72
+ (sequence.as(:td) >> str(",") >> sequence.as(:tds)) |
73
+ sequence.as(:td)
74
+ end
75
+
46
76
  rule(:sequence) do
47
77
  (lparen >> expression >> rparen).as(:intermediate_exp) |
48
78
  (binary_functions >> sequence.as(:base) >> sequence.maybe.as(:exponent)).as(:binary) |
@@ -53,16 +83,18 @@ module Plurimath
53
83
  end
54
84
 
55
85
  rule(:iteration) do
56
- (sequence.as(:dividend) >> str("mod").as(:mod) >> sequence.as(:divisor)).as(:mod) |
86
+ table.as(:table) |
87
+ (sequence.as(:dividend) >> str("mod").as(:mod) >> sequence.as(:divisor)).as(:mod) |
57
88
  (sequence >> base >> sequence.as(:base) >> power >> sequence.as(:exponent)).as(:power_base) |
58
89
  (sequence >> base >> sequence).as(:base) |
59
90
  (sequence >> power >> sequence).as(:power) |
60
91
  sequence.as(:sequence) |
61
- str(" ")
92
+ space
62
93
  end
63
94
 
64
95
  rule(:expression) do
65
- (iteration >> expression).as(:expr) |
96
+ left_right.as(:left_right) |
97
+ (iteration >> expression).as(:expr) |
66
98
  (iteration >> str("/").as(:/) >> iteration).as(:expr) |
67
99
  str("")
68
100
  end
@@ -70,9 +102,11 @@ module Plurimath
70
102
  root :expression
71
103
 
72
104
  def arr_to_expression(arr, name = nil)
105
+ type = arr.first.class
73
106
  arr.reduce do |expression, expr_string|
74
- expression = str(expression).as(name || expression) if expression.is_a?(Symbol)
75
- expression | str(expr_string).as(name || expr_string)
107
+ as_value = name.nil? ? expr_string || expression : name
108
+ expression = str(expression).as(as_value) if expression.is_a?(type)
109
+ expression | str(expr_string).as(as_value)
76
110
  end
77
111
  end
78
112
 
@@ -14,10 +14,11 @@ module Plurimath
14
14
 
15
15
  def parse
16
16
  nodes = Parse.new.parse(text)
17
- transformed_tree = Plurimath::Asciimath::Transform.new.apply(nodes)
18
- return transformed_tree if transformed_tree.is_a?(Plurimath::Math::Formula)
17
+ nodes = JSON.parse(nodes.to_json, symbolize_names: true)
18
+ transformed_tree = Transform.new.apply(nodes)
19
+ return transformed_tree if transformed_tree.is_a?(Math::Formula)
19
20
 
20
- Plurimath::Math::Formula.new(transformed_tree)
21
+ Math::Formula.new(transformed_tree)
21
22
  end
22
23
  end
23
24
  end