plurimath 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +13 -0
  3. data/.github/workflows/release.yml +22 -0
  4. data/.hound.yml +5 -0
  5. data/.rubocop.yml +8 -0
  6. data/AsciiMath-Supported-Data.adoc +1994 -274
  7. data/Gemfile +2 -0
  8. data/Latex-Supported-Data.adoc +1875 -1868
  9. data/MathML-Supported-Data.adoc +280 -263
  10. data/README.adoc +22 -20
  11. data/lib/plurimath/asciimath/constants.rb +186 -141
  12. data/lib/plurimath/asciimath/parse.rb +104 -39
  13. data/lib/plurimath/asciimath/parser.rb +3 -1
  14. data/lib/plurimath/asciimath/transform.rb +1074 -238
  15. data/lib/plurimath/html/parse.rb +1 -1
  16. data/lib/plurimath/latex/constants.rb +3228 -1432
  17. data/lib/plurimath/latex/parse.rb +106 -83
  18. data/lib/plurimath/latex/parser.rb +11 -4
  19. data/lib/plurimath/latex/transform.rb +354 -99
  20. data/lib/plurimath/math/base.rb +15 -0
  21. data/lib/plurimath/math/formula.rb +90 -13
  22. data/lib/plurimath/math/function/bar.rb +35 -1
  23. data/lib/plurimath/math/function/base.rb +25 -4
  24. data/lib/plurimath/math/function/binary_function.rb +101 -19
  25. data/lib/plurimath/math/function/cancel.rb +8 -0
  26. data/lib/plurimath/math/function/ceil.rb +3 -0
  27. data/lib/plurimath/math/function/color.rb +15 -5
  28. data/lib/plurimath/math/function/f.rb +8 -0
  29. data/lib/plurimath/math/function/fenced.rb +95 -8
  30. data/lib/plurimath/math/function/floor.rb +15 -0
  31. data/lib/plurimath/math/function/font_style/bold.rb +19 -0
  32. data/lib/plurimath/math/function/font_style/double_struck.rb +19 -0
  33. data/lib/plurimath/math/function/font_style/fraktur.rb +19 -0
  34. data/lib/plurimath/math/function/font_style/italic.rb +37 -0
  35. data/lib/plurimath/math/function/font_style/monospace.rb +19 -0
  36. data/lib/plurimath/math/function/font_style/normal.rb +37 -0
  37. data/lib/plurimath/math/function/font_style/sans-serif.rb +19 -0
  38. data/lib/plurimath/math/function/font_style/script.rb +19 -0
  39. data/lib/plurimath/math/function/font_style.rb +18 -5
  40. data/lib/plurimath/math/function/frac.rb +33 -3
  41. data/lib/plurimath/math/function/g.rb +7 -0
  42. data/lib/plurimath/math/function/hat.rb +12 -0
  43. data/lib/plurimath/math/function/inf.rb +21 -0
  44. data/lib/plurimath/math/function/int.rb +23 -2
  45. data/lib/plurimath/math/function/left.rb +25 -1
  46. data/lib/plurimath/math/function/lim.rb +40 -2
  47. data/lib/plurimath/math/function/limits.rb +9 -0
  48. data/lib/plurimath/math/function/log.rb +55 -4
  49. data/lib/plurimath/math/function/longdiv.rb +12 -0
  50. data/lib/plurimath/math/function/mbox.rb +31 -0
  51. data/lib/plurimath/math/function/menclose.rb +46 -0
  52. data/lib/plurimath/math/function/merror.rb +12 -0
  53. data/lib/plurimath/math/function/mod.rb +19 -4
  54. data/lib/plurimath/math/function/msgroup.rb +37 -0
  55. data/lib/plurimath/math/function/msline.rb +12 -0
  56. data/lib/plurimath/math/function/multiscript.rb +19 -0
  57. data/lib/plurimath/math/function/norm.rb +17 -1
  58. data/lib/plurimath/math/function/obrace.rb +17 -0
  59. data/lib/plurimath/math/function/oint.rb +2 -2
  60. data/lib/plurimath/math/function/over.rb +12 -5
  61. data/lib/plurimath/math/function/overset.rb +34 -5
  62. data/lib/plurimath/math/function/phantom.rb +28 -0
  63. data/lib/plurimath/math/function/power.rb +27 -9
  64. data/lib/plurimath/math/function/power_base.rb +109 -11
  65. data/lib/plurimath/math/function/prod.rb +25 -4
  66. data/lib/plurimath/math/function/right.rb +22 -2
  67. data/lib/plurimath/math/function/root.rb +23 -1
  68. data/lib/plurimath/math/function/rule.rb +33 -0
  69. data/lib/plurimath/math/function/scarries.rb +12 -0
  70. data/lib/plurimath/math/function/scarry.rb +12 -0
  71. data/lib/plurimath/math/function/sqrt.rb +23 -1
  72. data/lib/plurimath/math/function/stackrel.rb +27 -0
  73. data/lib/plurimath/math/function/substack.rb +7 -0
  74. data/lib/plurimath/math/function/sum.rb +50 -2
  75. data/lib/plurimath/math/function/sup.rb +3 -0
  76. data/lib/plurimath/math/function/table/align.rb +5 -5
  77. data/lib/plurimath/math/function/table/array.rb +25 -6
  78. data/lib/plurimath/math/function/table/bmatrix.rb +18 -7
  79. data/lib/plurimath/math/function/table/matrix.rb +13 -5
  80. data/lib/plurimath/math/function/table/multline.rb +5 -5
  81. data/lib/plurimath/math/function/table/pmatrix.rb +5 -5
  82. data/lib/plurimath/math/function/table/split.rb +5 -5
  83. data/lib/plurimath/math/function/table/vmatrix.rb +5 -6
  84. data/lib/plurimath/math/function/table.rb +185 -27
  85. data/lib/plurimath/math/function/td.rb +22 -9
  86. data/lib/plurimath/math/function/ternary_function.rb +74 -9
  87. data/lib/plurimath/math/function/text.rb +36 -11
  88. data/lib/plurimath/math/function/tr.rb +23 -4
  89. data/lib/plurimath/math/function/ubrace.rb +17 -0
  90. data/lib/plurimath/math/function/ul.rb +29 -0
  91. data/lib/plurimath/math/function/unary_function.rb +81 -8
  92. data/lib/plurimath/math/function/underline.rb +12 -0
  93. data/lib/plurimath/math/function/underover.rb +107 -0
  94. data/lib/plurimath/math/function/underset.rb +39 -0
  95. data/lib/plurimath/math/function/vec.rb +7 -1
  96. data/lib/plurimath/math/number.rb +5 -5
  97. data/lib/plurimath/math/symbol.rb +51 -12
  98. data/lib/plurimath/math/unicode.rb +11 -0
  99. data/lib/plurimath/math.rb +7 -3
  100. data/lib/plurimath/mathml/constants.rb +224 -147
  101. data/lib/plurimath/mathml/parser.rb +24 -8
  102. data/lib/plurimath/mathml/transform.rb +249 -153
  103. data/lib/plurimath/omml/parser.rb +24 -4
  104. data/lib/plurimath/omml/transform.rb +219 -157
  105. data/lib/plurimath/utility.rb +342 -20
  106. data/lib/plurimath/version.rb +1 -1
  107. metadata +21 -6
  108. data/.github/workflows/test.yml +0 -33
  109. data/lib/plurimath/mathml/parse.rb +0 -68
  110. data/lib/plurimath/omml/constants.rb +0 -154
@@ -4,212 +4,274 @@ require "parslet"
4
4
  module Plurimath
5
5
  class Omml
6
6
  class Transform < Parslet::Transform
7
- rule(r: simple(:r)) { r }
8
- rule(f: simple(:f)) { f }
9
- rule(d: simple(:d)) { Math::Function::Fenced.new(nil, [d], nil) }
10
- rule(val: simple(:val)) { val }
11
- rule(box: simple(:box)) { box }
12
- rule(rPr: simple(:rpr)) { nil }
13
- rule(rad: simple(:rad)) { rad }
14
- rule(sSub: simple(:sub)) { sub }
15
- rule(eqArr: simple(:arr)) { arr }
16
- rule(nary: simple(:nary)) { nary }
17
- rule(sSup: simple(:sSup)) { sSup }
18
- rule(sPre: simple(:sPre)) { sPre }
19
- rule(ctrlPr: simple(:ctrlPr)) { nil }
20
- rule(argSz: sequence(:argsz)) { nil }
21
- rule(rFonts: sequence(:fonts)) { nil }
22
- rule(sSubSup: simple(:sSubSup)) { sSubSup }
23
- rule(oMathPara: subtree(:omath)) { omath.drop(1) }
24
- rule(rPr: simple(:rpr), i: simple(:i)) { nil }
25
- rule(dPr: simple(:dpr), e: simple(:e)) { e }
26
- rule(dPr: simple(:dpr), e: sequence(:e)) { e }
27
- rule(boxPr: simple(:boxpr), e: simple(:e)) { e }
28
- rule(argPr: simple(:argPr), f: simple(:f)) { f }
29
- rule(rFonts: sequence(:fonts), i: simple(:i)) { nil }
30
- rule(type: sequence(:type), ctrlPr: simple(:ctrl)) { nil }
31
- rule(ascii: simple(:ascii), hAnsi: simple(:hansi)) { nil }
32
- rule(diff: sequence(:diff), ctrlPr: simple(:ctrl)) { nil }
33
- rule(limLoc: sequence(:limloc), ctrlPr: simple(:ctrl)) { limloc }
34
- rule(degHide: sequence(:degHide), ctrlPr: simple(:ctrl)) { nil }
35
-
36
- rule(oMath: subtree(:omath)) do
37
- omath.is_a?(Array) ? omath.drop(1) : omath
38
- end
39
-
40
- rule(d: sequence(:d)) do
41
- open_paren = d.shift if d.first.class_name == "symbol"
42
- close_paren = d.pop if d.last.class_name == "symbol"
43
- Math::Function::Fenced.new(
44
- open_paren,
45
- d,
46
- close_paren,
7
+ rule(t: simple(:t)) { Utility.text_classes(t) }
8
+ rule(e: subtree(:e)) { e.flatten.compact }
9
+ rule(i: sequence(:i)) { i }
10
+ rule(e: sequence(:e)) { e.flatten.compact }
11
+
12
+ rule(val: simple(:val)) { val }
13
+ rule(dPr: subtree(:dpr)) { dpr }
14
+ rule(num: subtree(:num)) { num }
15
+ rule(den: subtree(:den)) { den }
16
+ rule(rPr: subtree(:rPr)) { nil }
17
+ rule(fPr: subtree(:fPr)) { nil }
18
+ rule(mpr: subtree(:mpr)) { nil }
19
+ rule(mPr: subtree(:mPr)) { nil }
20
+ rule(box: subtree(:box)) { box.flatten.compact }
21
+ rule(deg: sequence(:deg)) { Utility.filter_values(deg) }
22
+ rule(sub: sequence(:sub)) { Utility.filter_values(sub) }
23
+ rule(sup: sequence(:sup)) { Utility.filter_values(sup) }
24
+ rule(boxPr: subtree(:box)) { nil }
25
+ rule(argPr: subtree(:arg)) { nil }
26
+ rule(accPr: subtree(:acc)) { acc.flatten.compact }
27
+
28
+ rule(sSubPr: subtree(:arg)) { nil }
29
+ rule(space: simple(:space)) { space }
30
+ rule(radPr: subtree(:radpr)) { nil }
31
+ rule(barPr: subtree(:barpr)) { barpr }
32
+ rule(oMath: subtree(:omath)) { omath.flatten.compact }
33
+ rule(fName: subtree(:fname)) { fname }
34
+ rule(oMath: sequence(:omath)) { omath }
35
+ rule(limLoc: simple(:limLoc)) { limLoc }
36
+ rule(begChr: simple(:begChr)) { Math::Symbol.new(begChr) }
37
+ rule(endChr: simple(:endChr)) { Math::Symbol.new(endChr) }
38
+
39
+ rule(rFonts: subtree(:rFonts)) { nil }
40
+ rule(sSupPr: subtree(:ssuppr)) { nil }
41
+ rule(sPrePr: subtree(:sprepr)) { nil }
42
+ rule(funcPr: subtree(:funcpr)) { nil }
43
+ rule(naryPr: subtree(:narypr)) { narypr }
44
+ rule(subHide: simple(:subHide)) { nil }
45
+ rule(supHide: simple(:supHide)) { nil }
46
+ rule(degHide: simple(:degHide)) { nil }
47
+ rule(ctrlPr: sequence(:ctrlpr)) { ctrlpr }
48
+ rule(eqArrPr: subtree(:eqarrpr)) { eqarrpr.flatten.compact }
49
+
50
+ rule(sequence: subtree(:sequence)) { sequence.flatten.compact }
51
+ rule(limUppPr: subtree(:limUppPr)) { nil }
52
+ rule(limLowPr: subtree(:limLowpr)) { nil }
53
+ rule(sequence: sequence(:sequence)) { sequence }
54
+ rule(sSubSupPr: subtree(:sSubSuppr)) { nil }
55
+
56
+ rule(groupChrPr: subtree(:groupchrpr)) { groupchrpr }
57
+ rule(borderBoxPr: subtree(:borderBoxpr)) { nil }
58
+ rule(lastRenderedPageBreak: sequence(:break)) { nil }
59
+
60
+ rule(f: subtree(:f)) do
61
+ Math::Function::Frac.new(
62
+ Utility.filter_values(f[1]),
63
+ Utility.filter_values(f[2]),
47
64
  )
48
65
  end
49
66
 
50
- rule(dPr: sequence(:dpr),
51
- e: simple(:e)) do
52
- [
53
- dpr[0],
54
- e,
55
- dpr[1],
56
- ]
67
+ rule(r: sequence(:r)) do
68
+ Math::Formula.new(
69
+ r.flatten.compact,
70
+ )
57
71
  end
58
72
 
59
- rule(sSub: sequence(:sub), r: simple(:r)) do
60
- [
61
- Math::Formula.new(
62
- sub.insert(1, r),
63
- ),
64
- ]
73
+ rule(m: sequence(:m)) do
74
+ Math::Function::Table.new(
75
+ m.flatten.compact,
76
+ )
65
77
  end
66
78
 
67
- rule(rPr: simple(:rpr), t: subtree(:t)) do
68
- Math::Function::Text.new(t.last)
79
+ rule(t: sequence(:t)) do
80
+ if t.empty?
81
+ Math::Function::Text.new
82
+ else
83
+ Utility.text_classes(t)
84
+ end
69
85
  end
70
86
 
71
- rule(r: simple(:r), d: simple(:d)) do
72
- Math::Formula.new(
73
- [
74
- r,
75
- d,
76
- ],
87
+ rule(d: subtree(:data)) do
88
+ fenced = data.flatten.compact
89
+ open_paren = fenced.shift if fenced.first.class_name == "symbol"
90
+ close_paren = fenced.shift if fenced.first.class_name == "symbol"
91
+ fenced_value = fenced
92
+ Math::Function::Fenced.new(
93
+ open_paren,
94
+ fenced_value,
95
+ close_paren,
77
96
  )
78
97
  end
79
98
 
80
- rule(eqArrPr: simple(:aqArrPr), e: sequence(:e)) do
81
- table_value = []
82
- e.each do |value|
83
- table_value << Math::Function::Tr.new(
84
- [
85
- Math::Function::Td.new(
86
- [
87
- value,
88
- ],
89
- ),
90
- ],
99
+ rule(mr: subtree(:mr)) do
100
+ row = []
101
+ mr.each do |td|
102
+ row << Math::Function::Td.new(
103
+ td.is_a?(Array) ? td : [td],
91
104
  )
92
105
  end
93
- Math::Function::Table.new(table_value)
106
+ Math::Function::Tr.new(row)
94
107
  end
95
108
 
96
- rule(rPr: simple(:rpr),
97
- t: simple(:text)) do
98
- if text.scan(/[[:digit:]]/).length == text.length
99
- Math::Number.new(text)
100
- elsif text.match?(/[[:alpha:]]/)
101
- Math::Function::Text.new(text)
109
+ rule(lim: sequence(:lim)) do
110
+ if lim.any?(String)
111
+ Utility.text_classes(lim)
102
112
  else
103
- Math::Symbol.new(text)
113
+ Utility.filter_values(lim)
104
114
  end
105
115
  end
106
116
 
107
- rule(radPr: simple(:rad),
108
- deg: simple(:deg),
109
- e: simple(:e)) do
110
- if deg.nil?
111
- Math::Function::Sqrt.new(e)
117
+ rule(acc: subtree(:acc)) do
118
+ acc_value = acc.flatten.compact
119
+ chr = Utility.find_pos_chr(acc_value, :chr)
120
+ chr_value = chr ? chr[:chr] : "^"
121
+ Math::Function::Overset.new(
122
+ Math::Symbol.new(chr_value),
123
+ Utility.filter_values(acc.last),
124
+ )
125
+ end
126
+
127
+ rule(func: subtree(:func)) do
128
+ func_name = func.flatten.compact
129
+ class_object = Utility.find_class_name(func_name.first)
130
+ if class_object
131
+ class_object.new(func_name.last)
112
132
  else
113
- Math::Function::Root.new(deg, e)
133
+ Math::Formula.new(
134
+ func_name,
135
+ )
114
136
  end
115
137
  end
116
138
 
117
- rule(fPr: simple(:fpr), num: simple(:num), den: simple(:den)) do
118
- Math::Function::Frac.new(num, den)
139
+ rule(nary: subtree(:nary)) do
140
+ Math::Formula.new(
141
+ [
142
+ Utility.nary_fonts(nary),
143
+ Utility.filter_values(nary[3]),
144
+ ],
145
+ )
119
146
  end
120
147
 
121
- rule(sSupPr: simple(:sSuppr), e: simple(:e), sup: simple(:power)) do
122
- Math::Function::Power.new(e, power)
148
+ rule(groupChr: subtree(:groupchr)) do
149
+ chr_pos = groupchr.first
150
+ pos = Utility.find_pos_chr(chr_pos, :pos)
151
+ chr = Utility.find_pos_chr(chr_pos, :chr)
152
+ if pos&.value?("top")
153
+ Math::Function::Overset.new(
154
+ Math::Symbol.new(chr ? chr[:chr] : ""),
155
+ Utility.filter_values(groupchr[1]),
156
+ )
157
+ else
158
+ Math::Function::Underset.new(
159
+ Math::Symbol.new(chr ? chr[:chr] : "⏟"),
160
+ Utility.filter_values(groupchr[1]),
161
+ )
162
+ end
123
163
  end
124
164
 
125
- rule(sSubPr: simple(:sSubpr), e: simple(:e), sub: simple(:base)) do
126
- Math::Function::Base.new(e, base)
165
+ rule(sSubSup: subtree(:sSubSup)) do
166
+ subsup = sSubSup.flatten.compact
167
+ subsup.each_with_index do |object, ind|
168
+ subsup[ind] = Utility.mathml_unary_classes([object]) if object.is_a?(String)
169
+ end
170
+ Math::Function::PowerBase.new(
171
+ subsup[0],
172
+ subsup[1],
173
+ subsup[2],
174
+ )
127
175
  end
128
176
 
129
- rule(chr: sequence(:chr),
130
- ctrlPr: simple(:ctrl)) do
131
- [nil] + chr
177
+ rule(sSup: subtree(:ssup)) do
178
+ sup = ssup.flatten.compact
179
+ Math::Function::Power.new(
180
+ sup[0],
181
+ sup[1],
182
+ )
132
183
  end
133
184
 
134
- rule(chr: sequence(:chr),
135
- limLoc: sequence(:limloc),
136
- ctrlPr: simple(:ctrl)) do
137
- limloc + chr
185
+ rule(rad: subtree(:rad)) do
186
+ if rad[1].nil?
187
+ Math::Function::Sqrt.new(
188
+ Utility.filter_values(rad[2]),
189
+ )
190
+ else
191
+ Math::Function::Root.new(
192
+ Utility.filter_values(rad[2]),
193
+ rad[1],
194
+ )
195
+ end
138
196
  end
139
197
 
140
- rule(begChr: sequence(:begChr),
141
- endChr: sequence(:endChr),
142
- ctrlPr: simple(:ctrl)) do
143
- [
144
- Math::Symbol.new(begChr.first),
145
- Math::Symbol.new(endChr.first),
146
- ]
198
+ rule(sSub: subtree(:ssub)) do
199
+ sub = ssub.flatten.compact
200
+ Plurimath::Math::Function::Base.new(
201
+ sub[0],
202
+ sub[1],
203
+ )
147
204
  end
148
205
 
149
- rule(chr: sequence(:chr),
150
- supHide: sequence(:supHide),
151
- ctrlPr: simple(:ctrl)) do
152
- [nil] + chr
206
+ rule(limUpp: subtree(:limUpp)) do
207
+ lim_values = limUpp.flatten.compact
208
+ first_value = lim_values[0]
209
+ Math::Function::Overset.new(
210
+ first_value,
211
+ lim_values[1],
212
+ )
153
213
  end
154
214
 
155
- rule(chr: sequence(:chr),
156
- limLoc: sequence(:limloc),
157
- supHide: sequence(:sup),
158
- ctrlPr: simple(:ctrl)) do
159
- limloc + chr
215
+ rule(limLow: subtree(:lim)) do
216
+ Math::Function::Underset.new(
217
+ Utility.filter_values(lim[1]),
218
+ Utility.filter_values(lim[2]),
219
+ )
160
220
  end
161
221
 
162
- rule(limLoc: sequence(:limloc),
163
- subHide: sequence(:sub),
164
- supHide: sequence(:sup),
165
- ctrlPr: simple(:ctrl)) do
166
- limloc
222
+ rule(borderBox: subtree(:box)) do
223
+ Math::Function::Menclose.new(
224
+ "longdiv",
225
+ Utility.filter_values(box[1]),
226
+ )
167
227
  end
168
228
 
169
- rule(chr: sequence(:chr),
170
- limLoc: sequence(:limloc),
171
- subHide: sequence(:sub),
172
- supHide: sequence(:sup),
173
- ctrlPr: simple(:ctrl)) do
174
- limloc + chr
229
+ rule(bar: subtree(:bar)) do
230
+ barpr = bar&.flatten&.compact
231
+ pospr = Utility.find_pos_chr(bar.first, :pos)
232
+ class_name = if pospr&.value?("top")
233
+ Math::Function::Bar
234
+ else
235
+ Math::Function::Ul
236
+ end
237
+ class_name.new(barpr.last)
175
238
  end
176
239
 
177
- rule(naryPr: sequence(:naryPr),
178
- sub: simple(:base),
179
- sup: simple(:power),
180
- e: simple(:e)) do
181
- fonts = Plurimath::Math::Symbol.new(naryPr[1] || "∫")
182
- first_value = if base.nil? && power.nil?
183
- fonts
184
- elsif naryPr.first == "undOvr"
185
- Math::Function::Underover.new(fonts, base, power)
186
- else
187
- Math::Function::PowerBase.new(fonts, base, power)
188
- end
189
- Math::Formula.new(
190
- [
191
- first_value,
192
- Math::Formula.new(
193
- [
194
- e,
195
- ],
196
- ),
197
- ],
240
+ rule(sPre: subtree(:spre)) do
241
+ pre = spre.flatten.compact
242
+ Math::Function::Multiscript.new(
243
+ pre[2],
244
+ pre[0],
245
+ pre[1],
198
246
  )
199
247
  end
200
248
 
201
- rule(sSubSupPr: simple(:sSubSuppr),
202
- e: simple(:e),
203
- sub: simple(:base),
204
- sup: simple(:power)) do
205
- Math::Function::PowerBase.new(e, base, power)
249
+ rule(eqArr: subtree(:eqArr)) do
250
+ table_value = []
251
+ eqArr.delete_at(0)
252
+ eqArr.each do |value|
253
+ table_value << Math::Function::Tr.new(
254
+ [
255
+ Math::Function::Td.new(
256
+ value.is_a?(Array) ? value : [value],
257
+ ),
258
+ ],
259
+ )
260
+ end
261
+ Math::Function::Table.new(table_value)
206
262
  end
207
263
 
208
- rule(sPrePr: simple(:sPrePr),
209
- e: simple(:e),
210
- sub: simple(:base),
211
- sup: simple(:power)) do
212
- Math::Function::Multiscript.new(e, base, power)
264
+ rule(ascii: simple(:ascii),
265
+ hAnsi: simple(:hansi)) do
266
+ nil
267
+ end
268
+
269
+ rule(attributes: simple(:attributes), value: sequence(:value)) do
270
+ if value.any? || attributes == "preserve"
271
+ value.any? ? value : [" "]
272
+ else
273
+ attributes
274
+ end
213
275
  end
214
276
  end
215
277
  end