plurimath 0.11.6 → 0.11.8

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/lib/plurimath/asciimath/utility.rb +50 -0
  3. data/lib/plurimath/asciimath.rb +1 -0
  4. data/lib/plurimath/formatter/numbers/mathml_renderer.rb +15 -15
  5. data/lib/plurimath/formatter/numbers/omml_renderer.rb +19 -19
  6. data/lib/plurimath/html/utility.rb +18 -0
  7. data/lib/plurimath/html.rb +1 -0
  8. data/lib/plurimath/latex/utility.rb +118 -0
  9. data/lib/plurimath/latex.rb +1 -0
  10. data/lib/plurimath/math/core.rb +12 -12
  11. data/lib/plurimath/math/formula.rb +10 -10
  12. data/lib/plurimath/math/function/abs.rb +3 -3
  13. data/lib/plurimath/math/function/bar.rb +14 -14
  14. data/lib/plurimath/math/function/base.rb +15 -7
  15. data/lib/plurimath/math/function/binary_function.rb +7 -7
  16. data/lib/plurimath/math/function/cancel.rb +2 -2
  17. data/lib/plurimath/math/function/ceil.rb +1 -1
  18. data/lib/plurimath/math/function/color.rb +2 -2
  19. data/lib/plurimath/math/function/ddot.rb +3 -3
  20. data/lib/plurimath/math/function/dot.rb +8 -8
  21. data/lib/plurimath/math/function/fenced.rb +8 -8
  22. data/lib/plurimath/math/function/floor.rb +1 -1
  23. data/lib/plurimath/math/function/font_style/bold.rb +2 -2
  24. data/lib/plurimath/math/function/font_style/double_struck.rb +2 -2
  25. data/lib/plurimath/math/function/font_style/fraktur.rb +2 -2
  26. data/lib/plurimath/math/function/font_style/italic.rb +2 -2
  27. data/lib/plurimath/math/function/font_style/monospace.rb +2 -2
  28. data/lib/plurimath/math/function/font_style/normal.rb +2 -2
  29. data/lib/plurimath/math/function/font_style/sans-serif.rb +2 -2
  30. data/lib/plurimath/math/function/font_style/script.rb +2 -2
  31. data/lib/plurimath/math/function/font_style.rb +16 -15
  32. data/lib/plurimath/math/function/frac.rb +7 -7
  33. data/lib/plurimath/math/function/hat.rb +8 -8
  34. data/lib/plurimath/math/function/inf.rb +2 -2
  35. data/lib/plurimath/math/function/int.rb +8 -8
  36. data/lib/plurimath/math/function/left.rb +5 -5
  37. data/lib/plurimath/math/function/lim.rb +3 -3
  38. data/lib/plurimath/math/function/limits.rb +2 -2
  39. data/lib/plurimath/math/function/linebreak.rb +2 -2
  40. data/lib/plurimath/math/function/log.rb +10 -10
  41. data/lib/plurimath/math/function/longdiv.rb +1 -1
  42. data/lib/plurimath/math/function/menclose.rb +2 -2
  43. data/lib/plurimath/math/function/merror.rb +2 -2
  44. data/lib/plurimath/math/function/mod.rb +1 -1
  45. data/lib/plurimath/math/function/mpadded.rb +2 -2
  46. data/lib/plurimath/math/function/ms.rb +2 -2
  47. data/lib/plurimath/math/function/msgroup.rb +2 -2
  48. data/lib/plurimath/math/function/multiscript.rb +2 -2
  49. data/lib/plurimath/math/function/nary.rb +15 -15
  50. data/lib/plurimath/math/function/none.rb +1 -1
  51. data/lib/plurimath/math/function/norm.rb +1 -1
  52. data/lib/plurimath/math/function/obrace.rb +8 -8
  53. data/lib/plurimath/math/function/oint.rb +6 -6
  54. data/lib/plurimath/math/function/over.rb +5 -5
  55. data/lib/plurimath/math/function/overleftrightarrow.rb +7 -7
  56. data/lib/plurimath/math/function/overset.rb +5 -5
  57. data/lib/plurimath/math/function/phantom.rb +9 -9
  58. data/lib/plurimath/math/function/power.rb +5 -5
  59. data/lib/plurimath/math/function/power_base.rb +5 -5
  60. data/lib/plurimath/math/function/prod.rb +4 -4
  61. data/lib/plurimath/math/function/right.rb +5 -5
  62. data/lib/plurimath/math/function/root.rb +7 -7
  63. data/lib/plurimath/math/function/rule.rb +2 -2
  64. data/lib/plurimath/math/function/scarries.rb +1 -1
  65. data/lib/plurimath/math/function/semantics.rb +4 -4
  66. data/lib/plurimath/math/function/sqrt.rb +7 -7
  67. data/lib/plurimath/math/function/stackrel.rb +5 -5
  68. data/lib/plurimath/math/function/substack.rb +1 -1
  69. data/lib/plurimath/math/function/sum.rb +4 -4
  70. data/lib/plurimath/math/function/sup.rb +1 -1
  71. data/lib/plurimath/math/function/table/array.rb +1 -1
  72. data/lib/plurimath/math/function/table/bmatrix.rb +2 -2
  73. data/lib/plurimath/math/function/table/matrix.rb +2 -2
  74. data/lib/plurimath/math/function/table.rb +29 -29
  75. data/lib/plurimath/math/function/td.rb +3 -3
  76. data/lib/plurimath/math/function/ternary_function.rb +13 -13
  77. data/lib/plurimath/math/function/text.rb +4 -4
  78. data/lib/plurimath/math/function/tilde.rb +6 -6
  79. data/lib/plurimath/math/function/tr.rb +4 -4
  80. data/lib/plurimath/math/function/ubrace.rb +1 -1
  81. data/lib/plurimath/math/function/ul.rb +9 -9
  82. data/lib/plurimath/math/function/unary_function.rb +14 -14
  83. data/lib/plurimath/math/function/underover.rb +1 -1
  84. data/lib/plurimath/math/function/underset.rb +12 -12
  85. data/lib/plurimath/math/function/unitsml.rb +1 -1
  86. data/lib/plurimath/math/function/vec.rb +7 -7
  87. data/lib/plurimath/math/model_helper.rb +103 -0
  88. data/lib/plurimath/math/number.rb +2 -2
  89. data/lib/plurimath/math/symbols/symbol.rb +2 -2
  90. data/lib/plurimath/math/symbols/tilde.rb +1 -1
  91. data/lib/plurimath/math.rb +1 -0
  92. data/lib/plurimath/mathml/constants.rb +29 -155
  93. data/lib/plurimath/mathml/formula_transformation.rb +2 -5
  94. data/lib/plurimath/mathml/translator.rb +6 -0
  95. data/lib/plurimath/mathml/utility.rb +53 -0
  96. data/lib/plurimath/mathml.rb +1 -0
  97. data/lib/plurimath/omml/formula_transformation.rb +5 -5
  98. data/lib/plurimath/omml/translator.rb +8 -5
  99. data/lib/plurimath/omml/utility.rb +109 -0
  100. data/lib/plurimath/omml.rb +1 -0
  101. data/lib/plurimath/unicode_math/transform.rb +5 -1
  102. data/lib/plurimath/unicode_math/utility.rb +256 -0
  103. data/lib/plurimath/unicode_math.rb +1 -0
  104. data/lib/plurimath/utility/intent_encoding.rb +4 -4
  105. data/lib/plurimath/utility.rb +6 -815
  106. data/lib/plurimath/version.rb +1 -1
  107. data/lib/plurimath/xml_engine/oga/dumper.rb +8 -1
  108. data/lib/plurimath/xml_engine/ox_engine.rb +4 -1
  109. data/lib/plurimath/xml_helper.rb +47 -0
  110. data/lib/plurimath.rb +1 -0
  111. data/plurimath.gemspec +5 -0
  112. metadata +24 -2
@@ -96,13 +96,6 @@ module Plurimath
96
96
  ln
97
97
  lg
98
98
  ].freeze
99
- MUNDER_CLASSES = %w[
100
- ubrace
101
- obrace
102
- right
103
- max
104
- min
105
- ].freeze
106
99
  PARENTHESIS = {
107
100
  "〈": "〉",
108
101
  "⌊": "⌋",
@@ -115,28 +108,6 @@ module Plurimath
115
108
  "{": "}",
116
109
  "[": "]",
117
110
  }.freeze
118
- TEXT_CLASSES = %w[
119
- symbol
120
- number
121
- text
122
- ].freeze
123
- TABLE_SUPPORTED_ATTRS = %i[
124
- columnlines
125
- rowlines
126
- frame
127
- ].freeze
128
- MPADDED_ATTRS = %i[
129
- height
130
- depth
131
- width
132
- ].freeze
133
- MGLYPH_ATTRS = %i[
134
- height
135
- width
136
- index
137
- alt
138
- src
139
- ].freeze
140
111
  UNICODEMATH_MENCLOSE_FUNCTIONS = {
141
112
  underline: "bottom",
142
113
  underbar: "bottom",
@@ -161,104 +132,14 @@ module Plurimath
161
132
  }.freeze
162
133
 
163
134
  class << self
164
- def organize_table(array, column_align: nil, options: nil)
165
- table = []
166
- table_data = []
167
- table_row = []
168
- organize_options(array, column_align) if options
169
- string_columns = column_align&.map { |column| column.is_a?(Math::Symbols::Paren) ? "|" : column.value }
170
- array.each do |data|
171
- if data&.separate_table
172
- table_row << Math::Function::Td.new(filter_table_data(table_data).compact)
173
- table_data = []
174
- if data.linebreak?
175
- organize_tds(table_row.flatten, string_columns.dup, options)
176
- table << Math::Function::Tr.new(table_row)
177
- table_row = []
178
- end
179
- next
180
- end
181
- table_data << data
182
- end
183
- table_row << Math::Function::Td.new(table_data.compact) if table_data
184
- unless table_row.nil? || table_row.empty?
185
- organize_tds(table_row.flatten, string_columns.dup, options)
186
- table << Math::Function::Tr.new(table_row)
187
- end
188
- unless column_align.nil? || column_align.empty?
189
- table_separator(string_columns, table,
190
- symbol: "|")
191
- end
192
- table
193
- end
194
-
195
- def organize_options(table_data, column_align)
196
- return column_align if column_align.length <= 1
197
-
198
- align = [column_align&.shift]
199
- table_data.insert(0, *column_align)
200
- align
201
- end
202
-
203
- def table_options(table_data)
204
- rowline = ""
205
- table_data.map do |tr|
206
- if tr&.parameter_one&.first&.parameter_one&.first.is_a?(Math::Symbols::Hline)
207
- rowline += "solid "
208
- else
209
- rowline += "none "
210
- end
211
- end
212
- options = { rowline: rowline.strip } if rowline.include?("solid")
213
- options || {}
214
- end
215
-
216
- def organize_tds(tr_array, column_align, options)
217
- return tr_array if column_align.nil? || column_align.empty?
218
-
219
- column_align.reject! { |string| string == "|" }
220
- column_align = column_align * tr_array.length if options
221
- tr_array.map.with_index do |td, ind|
222
- columnalign = ALIGNMENT_LETTERS[column_align[ind]&.to_sym]
223
- td.parameter_two = { columnalign: columnalign } if columnalign
224
- end
225
- end
226
-
227
- def filter_table_data(table_data)
228
- table_data.each_with_index do |object, ind|
229
- if object.is_a?(Math::Symbols::Minus)
230
- table_data[ind] = Math::Formula.new(
231
- [object, table_data.delete_at(ind.next)],
232
- )
233
- end
234
- end
235
- table_data
236
- end
237
-
238
135
  def get_table_class(text)
239
136
  Object.const_get("Plurimath::Math::Function::Table::#{capitalize(text)}")
240
137
  end
241
138
 
242
- def sub_sup_method?(sub_sup)
243
- if sub_sup.methods.include?(:class_name)
244
- Html::Constants::SUB_SUP_CLASSES.value?(sub_sup.class_name.to_sym)
245
- end
246
- end
247
-
248
139
  def get_class(text)
249
140
  Object.const_get("Plurimath::Math::Function::#{capitalize(text)}")
250
141
  end
251
142
 
252
- def get_symbol_class(text)
253
- Object.const_get("Plurimath::Math::Symbols::#{capitalize(text)}")
254
- rescue StandardError
255
- get_paren_class(text)
256
- end
257
-
258
- def get_paren_class(text)
259
- Object.const_get("Plurimath::Math::Symbols::Paren::#{capitalize(text)}")
260
- end
261
-
262
143
  def capitalize(text)
263
144
  text.to_s.split("_").map(&:capitalize).join
264
145
  end
@@ -308,43 +189,6 @@ module Plurimath
308
189
  symbols_hash(lang).merge(parens_hash(lang))
309
190
  end
310
191
 
311
- def ox_element(node, attributes: [], namespace: "")
312
- namespace = "#{namespace}:" unless namespace.empty?
313
-
314
- element = Plurimath.xml_engine.new_element("#{namespace}#{node}")
315
- element.set_attr(attributes)
316
- element
317
- end
318
-
319
- def rpr_element(wi_tag: false)
320
- rpr_element = ox_element("rPr", namespace: "w")
321
- attributes = { "w:ascii": "Cambria Math", "w:hAnsi": "Cambria Math" }
322
- rpr_element << ox_element(
323
- "rFonts",
324
- namespace: "w",
325
- attributes: attributes,
326
- )
327
- rpr_element << ox_element("i", namespace: "w") if wi_tag
328
- rpr_element
329
- end
330
-
331
- def update_nodes(element, nodes)
332
- nodes&.each do |node|
333
- next update_nodes(element, node) if node.is_a?(Array)
334
-
335
- element << node unless node.nil?
336
- end
337
- element
338
- end
339
-
340
- def pr_element(main_tag, wi_tag = false, namespace: "")
341
- tag_name = "#{main_tag}Pr"
342
- ox_element(
343
- tag_name,
344
- namespace: namespace,
345
- ) << rpr_element(wi_tag: wi_tag)
346
- end
347
-
348
192
  def filter_values(array, new_formula: true)
349
193
  return array unless array.is_a?(Array) || array.is_a?(Math::Formula)
350
194
 
@@ -355,96 +199,14 @@ module Plurimath
355
199
  array.first
356
200
  end
357
201
 
358
- def text_classes(text, lang:)
359
- return nil unless text
360
-
361
- text = filter_values(text) unless text.is_a?(String)
362
- return text if text.is_a?(Math::Core)
363
-
364
- if text&.scan(/[[:digit:]]/)&.length == text&.length
365
- Math::Number.new(text)
366
- elsif text&.match?(/[a-zA-Z]/)
367
- Math::Function::Text.new(text, lang: lang)
368
- else
369
- text = string_to_html_entity(text)
370
- .gsub("&#x26;", "&")
371
- .gsub("&#x3c;", "<")
372
- .gsub("&#x27;", "'")
373
- .gsub("&#xa0;", " ")
374
- .gsub("&#x3e;", ">")
375
- .gsub("&#xa;", "\n")
376
- symbols_class(text, lang: lang)
377
- end
378
- end
379
-
380
- def nary_fonts(nary, lang:)
381
- narypr = nary.first.flatten.compact
382
- subsup = narypr.any?("undOvr") ? "undOvr" : "subSup"
383
- unicode = narypr.any?(Hash) ? narypr.first[:chr] : "∫"
384
- Math::Function::Nary.new(
385
- symbols_class(string_to_html_entity(unicode), lang: lang),
386
- filter_values(nary[1]),
387
- filter_values(nary[2]),
388
- filter_values(nary[3]),
389
- { type: subsup },
390
- )
391
- end
392
-
393
- def find_pos_chr(fonts_array, key)
394
- fonts_array.find { |d| d.is_a?(Hash) && d[key] }
395
- end
396
-
397
- def td_values(objects, slicer)
398
- sliced = objects.slice_when { |object, _| symbol_value(object, slicer) }
399
- tds = sliced.map do |slice|
400
- Math::Function::Td.new(
401
- slice.delete_if { |d| symbol_value(d, slicer) }.compact,
402
- )
403
- end
404
- tds << Math::Function::Td.new([]) if symbol_value(objects.last, slicer)
405
- tds
406
- end
407
-
408
202
  def symbol_value(object, value)
409
- (object.is_a?(Math::Symbols::Comma) if value&.include?(",")) ||
410
- (object.is_a?(Math::Symbols::Minus) if value&.include?("-")) ||
411
- (object.is_a?(Math::Symbols::Paren::Vert) if value&.include?("|")) ||
412
- (object.is_a?(Math::Symbols::Symbol) && object&.value&.include?(value)) ||
413
- (value == "\\\\" && object.is_a?(Math::Function::Linebreak))
414
- end
415
-
416
- def td_value(td_object)
417
- str_classes = [String, Parslet::Slice]
418
- if str_classes.include?(td_object.class) && td_object.to_s.empty?
419
- return Math::Function::Text.new(nil)
420
- end
421
-
422
- td_object
423
- end
203
+ return false if value.nil?
424
204
 
425
- def mathml_unary_classes(text_array, omml: false, unicode_only: false,
426
- lang: nil)
427
- return [] if text_array.empty?
428
-
429
- compacted = text_array.compact
430
- return filter_values(compacted) unless compacted.any?(String)
431
-
432
- string = compacted.join
433
- classes = Mathml::Constants::CLASSES
434
- unicode = string_to_html_entity(string)
435
- return symbols_class(unicode, lang: lang) if unicode_only && unicode
436
-
437
- symbol = Mathml::Constants::UNICODE_SYMBOLS[unicode.strip.to_sym]
438
- if classes.include?(symbol&.strip)
439
- get_class(symbol.strip).new
440
- elsif classes.any?(string&.strip) && !(lang == :mathml && Mathml::Constants.accent_word?(string))
441
- get_class(string.strip).new
442
- elsif omml
443
- text_classes(string,
444
- lang: lang)
445
- else
446
- symbols_class(unicode, lang: lang)
447
- end
205
+ (object.is_a?(Math::Symbols::Comma) if value == ",") ||
206
+ (object.is_a?(Math::Symbols::Minus) if value == "-") ||
207
+ (object.is_a?(Math::Symbols::Paren::Vert) if value == "|") ||
208
+ (object.is_a?(Math::Symbols::Symbol) && object.value == value) ||
209
+ (value == "\\\\" && object.is_a?(Math::Function::Linebreak))
448
210
  end
449
211
 
450
212
  def symbols_class(string, lang:, table: false)
@@ -490,105 +252,6 @@ lang: nil)
490
252
  value
491
253
  end
492
254
 
493
- def join_attr_value(attrs, value, unicode_only: false, lang: :mathml)
494
- if attrs.is_a?(Hash) && attrs.key?(:intent)
495
- [
496
- Math::Function::Intent.new(
497
- filter_values(join_attr_value(nil, value,
498
- unicode_only: unicode_only, lang: lang)),
499
- Math::Function::Text.new(attrs[:intent], lang: lang),
500
- ),
501
- ]
502
- elsif value.any?(String)
503
- new_value = mathml_unary_classes(value, unicode_only: unicode_only,
504
- lang: lang)
505
- array_value = Array(new_value)
506
- if attrs.nil?
507
- array_value
508
- else
509
- join_attr_value(attrs, array_value,
510
- unicode_only: unicode_only)
511
- end
512
- elsif attrs.nil?
513
- value
514
- elsif attrs.is_a?(String) && ["solid",
515
- "none"].include?(attrs.split.first.downcase)
516
- table_separator(attrs.split, value)
517
- elsif attrs.is_a?(Hash)
518
- if attrs.key?(:accent) || attrs.key?(:accentunder)
519
- attr_is_accent(attrs, value)
520
- elsif attrs.key?(:linebreak)
521
- Math::Function::Linebreak.new(value.first, attrs)
522
- elsif attrs.key?(:bevelled) || attrs.key?(:linethickness)
523
- Math::Function::Frac.new(value[0], value[1], attrs)
524
- elsif attrs.key?(:notation)
525
- value << attrs
526
- elsif attrs.key?(:separators)
527
- fenced = Math::Function::Fenced.new(
528
- symbol_object(attrs[:open] || "(", lang: lang),
529
- value,
530
- symbol_object(attrs[:close] || ")", lang: lang),
531
- )
532
- fenced.options = { separators: attrs[:separators] }
533
- fenced
534
- elsif TABLE_SUPPORTED_ATTRS.any? { |atr| attrs.key?(atr) }
535
- Math::Function::Table.new(value, nil, nil, attrs)
536
- elsif MPADDED_ATTRS.any? { |atr| attrs.key?(atr) }
537
- Math::Function::Mpadded.new(
538
- filter_values(value),
539
- attrs,
540
- )
541
- elsif MGLYPH_ATTRS.any? { |atr| attrs.key?(atr) }
542
- Math::Function::Mglyph.new(
543
- attrs,
544
- )
545
- end
546
- elsif attrs.is_a?(Math::Core)
547
- attr_is_function(attrs, value)
548
- end
549
- end
550
-
551
- def attr_is_accent(attrs, value)
552
- if value.last.is_a?(Math::Function::UnaryFunction)
553
- value.last.parameter_one = value.shift if value.length > 1
554
- value.last.attributes = attrs.transform_values do |v|
555
- v.to_s == "true"
556
- end
557
- end
558
- value
559
- end
560
-
561
- def attr_is_function(attrs, value)
562
- case attrs
563
- when Math::Function::Fenced
564
- attrs.parameter_two = value.compact
565
- attrs
566
- when Math::Function::FontStyle
567
- attrs.parameter_one = filter_values(value)
568
- attrs
569
- when Math::Function::Color
570
- color_value = filter_values(value)
571
- if attrs.parameter_two
572
- attrs.parameter_two.parameter_one = color_value
573
- else
574
- attrs.parameter_two = color_value
575
- end
576
- attrs
577
- end
578
- end
579
-
580
- def nil_to_none_object(value)
581
- return value unless value.any?(NilClass) || value.any? do |val|
582
- val.is_a?(Array) && val.empty?
583
- end
584
-
585
- value.each.with_index do |val, index|
586
- next unless val.nil? || val.is_a?(Array)
587
-
588
- value[index] = Math::Function::None.new
589
- end
590
- end
591
-
592
255
  def unfenced_value(object, paren_specific: false)
593
256
  case object
594
257
  when Math::Function::Fenced
@@ -615,25 +278,6 @@ lang: nil)
615
278
  object.options.empty?
616
279
  end
617
280
 
618
- def frac_values(object)
619
- case object
620
- when Math::Formula
621
- object.value.any? { |d| symbol_value(d, ",") }
622
- when Array
623
- object.any? { |d| symbol_value(d, ",") }
624
- end
625
- end
626
-
627
- def table_td(object)
628
- new_object = case object
629
- when Math::Function::Td
630
- object
631
- else
632
- Math::Function::Td.new([object])
633
- end
634
- Array(new_object)
635
- end
636
-
637
281
  def symbol_object(value, lang: nil)
638
282
  value = case value
639
283
  when "ℒ" then "{:"
@@ -645,384 +289,6 @@ lang: nil)
645
289
  symbols_class(value, lang: lang)
646
290
  end
647
291
 
648
- def asciimath_symbol_object(value, lang: :asciimath)
649
- return if value.nil?
650
-
651
- symbols = symbols_hash(lang)
652
- return symbols[value&.to_s].new if symbols.key?(value&.to_s)
653
-
654
- symbol_object(value&.to_s, lang: lang)
655
- end
656
-
657
- def validate_left_right(fields = [])
658
- fields.each do |field|
659
- if field.is_a?(Math::Formula) && field.value.first.is_a?(Math::Function::Left)
660
- field.left_right_wrapper = true
661
- end
662
- end
663
- end
664
-
665
- def left_right_objects(paren, function)
666
- paren = if paren.to_s.match?(/\\\{|\\\}/)
667
- paren.to_s.gsub("\\", "")
668
- else
669
- Latex::Constants::LEFT_RIGHT_PARENTHESIS[paren.to_sym]
670
- end
671
- get_class(function).new(paren)
672
- end
673
-
674
- def valid_class(object)
675
- text = object.extract_class_name_from_text
676
- (object.extractable? && Asciimath::Constants::SUB_SUP_CLASSES.include?(text)) ||
677
- Latex::Constants::SYMBOLS[text.to_sym] == :power_base
678
- end
679
-
680
- def mrow_left_right(mrow = [])
681
- object = mrow.first
682
- !(
683
- (object.is_a?(Math::Function::TernaryFunction) && object.any_value_exist? && (mrow.length <= 2)) ||
684
- (object.is_a?(Math::Function::UnaryFunction) && mrow.length == 1)
685
- )
686
- end
687
-
688
- def populate_function_classes(mrow = [], lang:)
689
- flatten_mrow = mrow.flatten.compact
690
- unary_function_classes(flatten_mrow, lang: lang)
691
- binary_function_classes(flatten_mrow, lang: lang)
692
- ternary_function_classes(flatten_mrow)
693
- flatten_mrow
694
- end
695
-
696
- def binary_function_classes(mrow, lang:, under: false)
697
- binary_class = Math::Function::BinaryFunction
698
- mrow.each_with_index do |object, ind|
699
- if object.is_a?(String)
700
- mrow[ind] =
701
- mathml_unary_classes([object], lang: lang)
702
- end
703
- object = mrow[ind]
704
- next unless object.is_a?(binary_class)
705
-
706
- if object.is_a?(Math::Function::Mod)
707
- next unless mrow.length >= 1
708
-
709
- object.parameter_one = mrow.delete_at(ind - 1) unless ind.zero?
710
- object.parameter_two = mrow.delete_at(ind)
711
- elsif Mathml::Constants::UNICODE_SYMBOLS.invert[object.class_name] && mrow.length > 1
712
- next if object.parameter_one || mrow.length > 2
713
- next object.parameter_one = mrow.delete_at(ind - 1) if under && ind <= 1
714
-
715
- object.parameter_one = mrow.delete_at(ind + 1)
716
- end
717
- end
718
- end
719
-
720
- def unary_function_classes(mrow, lang:)
721
- unary_class = Math::Function::UnaryFunction
722
- if mrow.any?(String) || mrow.any?(unary_class)
723
- mrow.each_with_index do |object, ind|
724
- if object.is_a?(String)
725
- mrow[ind] =
726
- mathml_unary_classes([object], lang: lang)
727
- end
728
- object = mrow[ind] if object.is_a?(String)
729
- next unless object.is_a?(unary_class)
730
- next if object.is_a?(Math::Function::Text)
731
- next if object.parameter_one || mrow[ind + 1].nil?
732
- next unless ind.zero?
733
-
734
- object.parameter_one = mrow.delete_at(ind + 1)
735
- end
736
- end
737
- end
738
-
739
- def ternary_function_classes(mrow)
740
- ternary_class = Math::Function::TernaryFunction
741
- if mrow.any?(ternary_class) && mrow.length > 1
742
- mrow.each_with_index do |object, ind|
743
- if object.is_a?(ternary_class)
744
- next if [Math::Function::Fenced, Math::Function::Multiscript].include?(object.class)
745
- next unless object.parameter_one || object.parameter_two
746
- next if object.parameter_three
747
-
748
- object.parameter_three = filter_values(mrow.delete_at(ind + 1))
749
- end
750
- end
751
- end
752
- end
753
-
754
- def paren_able?(arr = [], mrow = [])
755
- arr.any? do |opening, closing|
756
- symbol_value(mrow.first,
757
- opening.to_s) && symbol_value(mrow.last, closing.to_s)
758
- end
759
- end
760
-
761
- def fenceable_classes(mrow = [])
762
- return false unless mrow.length > 1
763
- return unless paren_able?(PARENTHESIS,
764
- mrow) || (mrow.first.is_a?(Math::Symbols::Paren) && mrow.last.is_a?(Math::Symbols::Paren))
765
-
766
- open_paren = mrow.shift
767
- close_paren = mrow.pop
768
- if mrow.length == 1 && mrow.first.is_a?(Math::Function::Table)
769
- table = mrow.first
770
- table.open_paren = open_paren
771
- table.close_paren = close_paren
772
- else
773
- mrow.replace(
774
- [
775
- Math::Function::Fenced.new(open_paren, mrow.dup, close_paren),
776
- ],
777
- )
778
- end
779
- end
780
-
781
- def validate_math_zone(object, lang:, intent: false, options: nil)
782
- return false unless object
783
-
784
- if object.is_a?(Math::Formula)
785
- filter_math_zone_values(object.value, lang: lang, intent: intent,
786
- options: options).find do |value|
787
- !(value.is_a?(Math::Function::Text) || value.is_a?(Math::Symbols::Symbol))
788
- end
789
- else
790
- !(TEXT_CLASSES.include?(object.class_name) || object.is_a?(Math::Symbols::Symbol))
791
- end
792
- end
793
-
794
- def filter_math_zone_values(value, lang:, intent: false, options: nil)
795
- return [] if value&.empty?
796
-
797
- new_arr = []
798
- temp_array = []
799
- skip_index = nil
800
- value.each_with_index do |obj, index|
801
- object = obj.dup
802
- next if index == skip_index
803
- if TEXT_CLASSES.include?(object.class_name) || math_display_text_objects(object)
804
- next temp_array << (if object.is_a?(Math::Symbols::Symbol)
805
- symbol_to_text(
806
- object, lang: lang, intent: intent, options: options
807
- )
808
- else
809
- object.value
810
- end)
811
- end
812
-
813
- if temp_array.any?
814
- new_arr << Math::Function::Text.new(temp_array.join(" "),
815
- lang: lang)
816
- end
817
- temp_array = []
818
- new_arr << object
819
- end
820
- if temp_array.any?
821
- new_arr << Math::Function::Text.new(temp_array.join(" "),
822
- lang: lang)
823
- end
824
- new_arr
825
- end
826
-
827
- def symbol_to_text(symbol, lang:, options:, intent: false)
828
- case lang
829
- when :asciimath
830
- symbol.to_asciimath(options: options)
831
- when :latex
832
- symbol.to_latex(options: options)
833
- when :mathml
834
- symbol.to_mathml_without_math_tag(intent,
835
- options: options).nodes.first
836
- when :omml
837
- symbol.to_omml_without_math_tag(true, options: options)
838
- when :unicodemath
839
- symbol.to_unicodemath(options: options)
840
- end
841
- end
842
-
843
- def unicode_accents(accents, lang: :unicodemath)
844
- if accents.is_a?(Math::Function::BinaryFunction)
845
- accents
846
- elsif accents.any? { |acc| acc&.dig(:first_value)&.is_a?(Array) }
847
- accent_value = accents.first[:first_value].pop
848
- first_value = accents.first[:first_value]
849
- accents.first[:first_value] = accent_value
850
- Math::Formula.new(
851
- first_value + [transform_accents(accents, lang: lang)],
852
- )
853
- else
854
- transform_accents(accents, lang: lang)
855
- end
856
- end
857
-
858
- def transform_accents(accents, lang:)
859
- accents.reduce do |function, accent|
860
- if function.is_a?(Hash)
861
- if function[:prime_accent_symbols]
862
- Math::Function::Power.new(
863
- unfenced_value(
864
- accent_value(function, function: true,
865
- lang: lang), paren_specific: true
866
- ),
867
- accent_value(accent, lang: lang),
868
- )
869
- else
870
- Math::Function::Overset.new(
871
- accent_value(accent, lang: lang),
872
- unfenced_value(
873
- accent_value(function, function: true,
874
- lang: lang), paren_specific: true
875
- ),
876
- { accent: true },
877
- )
878
- end
879
- elsif accent[:prime_accent_symbols]
880
- Math::Function::Power.new(
881
- unfenced_value(function, paren_specific: true),
882
- accent_value(accent, lang: lang),
883
- )
884
- else
885
- Math::Function::Overset.new(
886
- accent_value(accent, lang: lang),
887
- unfenced_value(function, paren_specific: true),
888
- { accent: true },
889
- )
890
- end
891
- end
892
- end
893
-
894
- def accent_value(accent, lang:, function: false)
895
- if accent[:accent_symbols]
896
- symbols_class(
897
- UnicodeMath::Constants::ACCENT_SYMBOLS[accent[:accent_symbols].to_sym] || accent[:accent_symbols], lang: lang
898
- )
899
- else
900
- accent[:first_value] || filter_values(accent[:prime_accent_symbols])
901
- end
902
- end
903
-
904
- def unicode_fractions(fractions)
905
- frac_arr = UnicodeMath::Constants::UNICODE_FRACTIONS[fractions.to_sym]
906
- Math::Function::Frac.new(
907
- Math::Number.new(frac_arr.first.to_s),
908
- Math::Number.new(frac_arr.last.to_s),
909
- { displaystyle: false, unicodemath_fraction: true },
910
- )
911
- end
912
-
913
- def updated_primes(prime)
914
- filter_values(
915
- prime.to_s.scan(UNICODE_REGEX).map do |str|
916
- symbols_class(str, lang: :unicodemath)
917
- end,
918
- )
919
- end
920
-
921
- def fractions(numerator, denominator, options = nil)
922
- frac_class = Math::Function::Frac
923
- if denominator.is_a?(frac_class)
924
- if denominator.parameter_one.is_a?(frac_class)
925
- recursion_fraction(denominator, numerator, options)
926
- else
927
- denominator.parameter_one = frac_class.new(
928
- unfenced_value(numerator, paren_specific: true),
929
- unfenced_value(denominator.parameter_one, paren_specific: true),
930
- options,
931
- )
932
- end
933
- denominator
934
- else
935
- frac_class.new(
936
- unfenced_value(numerator, paren_specific: true),
937
- unfenced_value(denominator, paren_specific: true),
938
- options,
939
- )
940
- end
941
- end
942
-
943
- def recursion_fraction(frac, numerator, options)
944
- frac_class = Math::Function::Frac
945
- new_numerator = frac.parameter_one
946
- if new_numerator.is_a?(frac_class)
947
- recursion_fraction(new_numerator, numerator, options)
948
- else
949
- frac.parameter_one = frac_class.new(
950
- unfenced_value(numerator, paren_specific: true),
951
- unfenced_value(frac.parameter_one, paren_specific: true),
952
- options,
953
- )
954
- frac
955
- end
956
- end
957
-
958
- def recursive_sub(sub_script, sub_recursion)
959
- base_class = Math::Function::Base
960
- if sub_recursion.is_a?(base_class)
961
- if sub_recursion.parameter_one.is_a?(base_class)
962
- base_recursion(sub_script, sub_recursion)
963
- else
964
- sub_recursion.parameter_one = base_class.new(sub_script,
965
- sub_recursion.parameter_one)
966
- end
967
- sub_recursion
968
- else
969
- base_class.new(
970
- sub_script,
971
- sub_recursion,
972
- )
973
- end
974
- end
975
-
976
- def base_recursion(sub_script, sub_recursion)
977
- base_class = Math::Function::Base
978
- new_sub = sub_recursion.parameter_one
979
- if new_sub.is_a?(base_class)
980
- base_recursion(sub_script, new_sub)
981
- else
982
- sub_recursion.parameter_one = base_class.new(sub_script, new_sub)
983
- end
984
- sub_recursion
985
- end
986
-
987
- def recursive_sup(sup_script, sup_recursion)
988
- power_class = Math::Function::Power
989
- if sup_recursion.is_a?(power_class)
990
- if sup_recursion.parameter_one.is_a?(power_class)
991
- sup_recursion(sup_script, sup_recursion)
992
- else
993
- sup_recursion.parameter_one = power_class.new(sup_script,
994
- sup_recursion.parameter_one)
995
- end
996
- sup_recursion
997
- else
998
- power_class.new(
999
- sup_script,
1000
- sup_recursion,
1001
- )
1002
- end
1003
- end
1004
-
1005
- def sup_recursion(sup_script, sup_recursion)
1006
- power_class = Math::Function::Power
1007
- new_sup = sup_recursion.parameter_one
1008
- if new_sup.is_a?(power_class)
1009
- sup_recursion(sup_script, new_sup)
1010
- else
1011
- sup_recursion.parameter_one = power_class.new(sup_script, new_sup)
1012
- end
1013
- sup_recursion
1014
- end
1015
-
1016
- def base_is_prime?(base)
1017
- symbol_prime?(base.parameter_two) ||
1018
- primes_constants.key(base.parameter_two.value)
1019
- end
1020
-
1021
- def symbol_prime?(obj)
1022
- obj&.class&.const_defined?(:INPUT) &&
1023
- primes_constants&.key(hexcode_in_input(obj))
1024
- end
1025
-
1026
292
  def primes_constants
1027
293
  primes = {}
1028
294
  primes
@@ -1036,81 +302,6 @@ lang: nil)
1036
302
  end
1037
303
  end
1038
304
 
1039
- def base_is_sub_or_sup?(base)
1040
- case base
1041
- when Math::Formula
1042
- base_is_sub_or_sup?(base.value.first)
1043
- when Math::Function::Fenced
1044
- base_is_sub_or_sup?(base.parameter_two.first)
1045
- when Math::Symbols::Symbol, Math::Number
1046
- base.mini_sub_sized || base_mini_sup_sized
1047
- end
1048
- end
1049
-
1050
- def identity_matrix(size)
1051
- matrix = Array.new(size) { Array.new(size, 0) }
1052
- size.times { |i| matrix[i][i] = 1 }
1053
- matrix.map do |tr|
1054
- tr.map.with_index do |td, i|
1055
- tr[i] = Math::Function::Td.new([Math::Number.new(td.to_s)])
1056
- end
1057
- Math::Function::Tr.new(tr)
1058
- end
1059
- end
1060
-
1061
- def enclosure_attrs(mask)
1062
- raise "enclosure mask is not between 0 and 255" if mask.nil? || mask.negative? || mask > 255
1063
-
1064
- ret = ""
1065
- unless mask.nil?
1066
- mask ^= 15
1067
- bin_mask = mask.to_s(2).reverse
1068
- classes = bin_mask.chars.each_with_index.filter_map do |bit, i|
1069
- MASK_CLASSES[2**i] if bit == "1"
1070
- end
1071
- ret = classes.join(" ")
1072
- end
1073
- ret
1074
- end
1075
-
1076
- def notations_to_mask(notations)
1077
- mask = notations.split.map do |notation|
1078
- MASK_CLASSES.key(notation)
1079
- end
1080
- mask.inject(*:+) ^ 15
1081
- end
1082
-
1083
- def slashed_values(value)
1084
- decoded = HTMLEntities.new.decode(value)
1085
- if decoded.to_s.match?(/^\w+/)
1086
- Math::Function::Text.new("\\#{decoded}")
1087
- else
1088
- Math::Symbols::Symbol.new(decoded, true)
1089
- end
1090
- end
1091
-
1092
- def sequence_slashed_values(values, lang:)
1093
- values.each.with_index do |value, index|
1094
- decoded = HTMLEntities.new.decode(value.value)
1095
- slashed = if index.zero?
1096
- slashed_values(value.value)
1097
- elsif decoded.match?(/[0-9]/)
1098
- Math::Number.new(decoded)
1099
- else
1100
- symbols_class(
1101
- decoded, lang: lang
1102
- )
1103
- end
1104
- values[index] = slashed
1105
- end
1106
- values
1107
- end
1108
-
1109
- def math_display_text_objects(object)
1110
- class_names = ["plus", "minus", "circ", "equal", "symbol"].freeze
1111
- class_names.include?(object.class_name)
1112
- end
1113
-
1114
305
  def latex_table_curly_paren(string)
1115
306
  case string
1116
307
  when "{" then Math::Symbols::Paren::Lcurly.new