plurimath 0.8.17 → 0.8.18

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/lib/plurimath/asciimath/parse.rb +1 -0
  4. data/lib/plurimath/asciimath/transform.rb +12 -0
  5. data/lib/plurimath/math/core.rb +63 -4
  6. data/lib/plurimath/math/formula/mrow.rb +193 -0
  7. data/lib/plurimath/math/formula/mstyle.rb +17 -0
  8. data/lib/plurimath/math/formula.rb +307 -4
  9. data/lib/plurimath/math/function/base.rb +4 -0
  10. data/lib/plurimath/math/function/color.rb +17 -4
  11. data/lib/plurimath/math/function/fenced.rb +219 -0
  12. data/lib/plurimath/math/function/frac.rb +4 -0
  13. data/lib/plurimath/math/function/linebreak.rb +2 -2
  14. data/lib/plurimath/math/function/longdiv.rb +3 -0
  15. data/lib/plurimath/math/function/menclose.rb +3 -0
  16. data/lib/plurimath/math/function/merror.rb +5 -2
  17. data/lib/plurimath/math/function/mglyph.rb +27 -0
  18. data/lib/plurimath/math/function/mlabeledtr.rb +19 -0
  19. data/lib/plurimath/math/function/mpadded.rb +28 -1
  20. data/lib/plurimath/math/function/ms.rb +80 -0
  21. data/lib/plurimath/math/function/msgroup.rb +15 -0
  22. data/lib/plurimath/math/function/msline.rb +5 -2
  23. data/lib/plurimath/math/function/multiscript.rb +14 -0
  24. data/lib/plurimath/math/function/over.rb +3 -0
  25. data/lib/plurimath/math/function/overset.rb +11 -0
  26. data/lib/plurimath/math/function/phantom.rb +3 -0
  27. data/lib/plurimath/math/function/power.rb +3 -0
  28. data/lib/plurimath/math/function/power_base.rb +3 -0
  29. data/lib/plurimath/math/function/root.rb +3 -0
  30. data/lib/plurimath/math/function/scarries.rb +3 -0
  31. data/lib/plurimath/math/function/semantics.rb +14 -0
  32. data/lib/plurimath/math/function/sqrt.rb +3 -0
  33. data/lib/plurimath/math/function/stackrel.rb +3 -0
  34. data/lib/plurimath/math/function/table.rb +53 -0
  35. data/lib/plurimath/math/function/td.rb +4 -1
  36. data/lib/plurimath/math/function/text.rb +22 -2
  37. data/lib/plurimath/math/function/tr.rb +13 -0
  38. data/lib/plurimath/math/function/underover.rb +3 -0
  39. data/lib/plurimath/math/function/underset.rb +44 -0
  40. data/lib/plurimath/math/number.rb +10 -1
  41. data/lib/plurimath/math/symbols/gg.rb +4 -4
  42. data/lib/plurimath/math/symbols/ll.rb +4 -4
  43. data/lib/plurimath/math/symbols/minus.rb +1 -1
  44. data/lib/plurimath/math/symbols/symbol.rb +12 -4
  45. data/lib/plurimath/math.rb +2 -0
  46. data/lib/plurimath/mathml/parser.rb +45 -86
  47. data/lib/plurimath/mathml/utility/empty_defined_methods.rb +477 -0
  48. data/lib/plurimath/mathml/utility/formula_transformation.rb +472 -0
  49. data/lib/plurimath/mathml/utility.rb +363 -0
  50. data/lib/plurimath/mathml.rb +1 -0
  51. data/lib/plurimath/unicode_math/transform.rb +2 -2
  52. data/lib/plurimath/utility.rb +5 -23
  53. data/lib/plurimath/version.rb +1 -1
  54. data/lib/plurimath.rb +9 -0
  55. data/plurimath.gemspec +4 -2
  56. metadata +37 -5
  57. data/lib/plurimath/mathml/transform.rb +0 -413
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../mathml/utility"
4
+
3
5
  module Plurimath
4
6
  module Math
5
7
  class Formula < Core
8
+ include Mathml::Utility
9
+
6
10
  attr_accessor :value, :left_right_wrapper, :displaystyle, :input_string, :unitsml, :unitsml_xml
7
11
 
8
12
  MATH_ZONE_TYPES = %i[
@@ -168,7 +172,7 @@ module Plurimath
168
172
 
169
173
  def to_display(type = nil, formatter: nil)
170
174
  options = { formatter: formatter }
171
- return type_error! unless MATH_ZONE_TYPES.include?(type.downcase.to_sym)
175
+ return type_error!(type) unless MATH_ZONE_TYPES.include?(type.downcase.to_sym)
172
176
 
173
177
  math_zone = case type
174
178
  when :asciimath
@@ -246,7 +250,7 @@ module Plurimath
246
250
  end
247
251
 
248
252
  def update(object)
249
- self.value = Array(object)
253
+ @value = Array(object).flatten.compact
250
254
  end
251
255
 
252
256
  def cloned_objects
@@ -292,7 +296,6 @@ module Plurimath
292
296
  update(Array(value) + values)
293
297
  end
294
298
 
295
-
296
299
  def mini_sized?
297
300
  true if value&.first&.mini_sized?
298
301
  end
@@ -304,8 +307,302 @@ module Plurimath
304
307
  }
305
308
  end
306
309
 
310
+ def element_order=(value)
311
+ @value = validated_order(value)
312
+ end
313
+
314
+ # Attributes start
315
+ def mathcolor=(value)
316
+ return if value.nil? || value.empty?
317
+
318
+ update(
319
+ [
320
+ Math::Function::Color.new(
321
+ Math::Function::Text.new(value),
322
+ filter_values(@value, array_to_instance: true),
323
+ )
324
+ ]
325
+ )
326
+ end
327
+
328
+ def mathvariant=(value)
329
+ return if value.nil? || value.empty?
330
+ return unless Plurimath::Utility::FONT_STYLES.key?(value.to_sym)
331
+
332
+ update(
333
+ [
334
+ Plurimath::Utility::FONT_STYLES[value.to_sym].new(
335
+ filter_values(@value, array_to_instance: true),
336
+ value,
337
+ )
338
+ ]
339
+ )
340
+ end
341
+
342
+ def intent=(value)
343
+ return unless value
344
+
345
+ self.content = nil
346
+ update(
347
+ [
348
+ Function::Intent.new(
349
+ filter_values(@value, array_to_instance: true),
350
+ Function::Text.new(value),
351
+ )
352
+ ]
353
+ )
354
+ end
355
+ # Attributes end
356
+
357
+ def ms_value=(value)
358
+ return if value.nil? || value.empty?
359
+
360
+ update(
361
+ replace_order_with_value(
362
+ @value,
363
+ Array(validate_symbols(value)),
364
+ "ms"
365
+ )
366
+ )
367
+ end
368
+
369
+ def mi_value=(value)
370
+ return if value.nil? || value.empty?
371
+
372
+ value = update_temp_mathml_values(value) if value.any? do |val|
373
+ val.is_a?(Math::Core) && val.temp_mathml_order.any?
374
+ end
375
+ update(
376
+ replace_order_with_value(
377
+ @value,
378
+ Array(validate_symbols(value)).flatten,
379
+ "mi"
380
+ )
381
+ )
382
+ end
383
+
384
+ def mn_value=(value)
385
+ return if value.nil? || value.empty?
386
+
387
+ update(
388
+ replace_order_with_value(
389
+ @value,
390
+ Array(validate_symbols(value)),
391
+ "mn"
392
+ )
393
+ )
394
+ end
395
+
396
+ def mtext_value=(value)
397
+ return if value.nil? || value.empty?
398
+
399
+ update(
400
+ replace_order_with_value(
401
+ @value,
402
+ Array(validate_symbols(value)),
403
+ "mtext"
404
+ )
405
+ )
406
+ end
407
+
408
+ def mo_value=(value)
409
+ return if value.nil? || value.empty?
410
+
411
+ value = update_temp_mathml_values(value)
412
+ update(
413
+ replace_order_with_value(
414
+ @value,
415
+ Array(value),
416
+ "mo"
417
+ )
418
+ )
419
+ end
420
+
421
+ def mstyle_value=(value)
422
+ return if value.empty?
423
+
424
+ update(
425
+ filter_values(
426
+ replace_order_with_value(
427
+ @value,
428
+ Array(filter_values(value, array_to_instance: true)),
429
+ "mstyle"
430
+ )
431
+ )
432
+ )
433
+ end
434
+
435
+ def mrow_value=(value)
436
+ return if value.nil? || value.empty?
437
+
438
+ replacing_order = value.length > 1 && value.any?(String)
439
+ update(
440
+ replace_order_with_value(
441
+ @value,
442
+ filter_values(
443
+ value,
444
+ array_to_instance: true,
445
+ replacing_order: replacing_order
446
+ ),
447
+ "mrow"
448
+ )
449
+ )
450
+ end
451
+
452
+ def munderover_value=(value)
453
+ update_temp_order(value, "munderover")
454
+ end
455
+
456
+ def msub_value=(value)
457
+ update_temp_order(value, "msub")
458
+ end
459
+
460
+ def msup_value=(value)
461
+ update_temp_order(value, "msup")
462
+ end
463
+
464
+ def mover_value=(value)
465
+ update_temp_order(value, "mover")
466
+ end
467
+
468
+ def munder_value=(value)
469
+ update_temp_order(value, "munder")
470
+ end
471
+
472
+ def msubsup_value=(value)
473
+ update_temp_order(value, "msubsup")
474
+ end
475
+
476
+ def mfrac_value=(value)
477
+ update_temp_order(value, "mfrac")
478
+ end
479
+
480
+ def msqrt_value=(value)
481
+ update_temp_order(value, "msqrt")
482
+ end
483
+
484
+ def mfenced_value=(value)
485
+ update_temp_order(value, "mfenced")
486
+ end
487
+
488
+ def mroot_value=(value)
489
+ update_temp_order(value, "mroot")
490
+ end
491
+
492
+ def msgroup_value=(value)
493
+ update_temp_order(value, "msgroup")
494
+ end
495
+
496
+ def mscarries_value=(value)
497
+ update_temp_order(value, "mscarries")
498
+ end
499
+
500
+ def msline_value=(value)
501
+ update_temp_order(value, "msline")
502
+ end
503
+
504
+ def msrow_value=(value)
505
+ update_temp_order(value, "msrow")
506
+ end
507
+
508
+ def semantics_value=(value)
509
+ update_temp_order(value, "semantics")
510
+ end
511
+
512
+ def mstack_value=(value)
513
+ update_temp_order(value, "mstack")
514
+ end
515
+
516
+ def merror_value=(value)
517
+ return if value.nil? || value.empty?
518
+
519
+ update(
520
+ replace_order_with_value(
521
+ @value,
522
+ filter_values(update_temp_mathml_values(value)),
523
+ "merror"
524
+ )
525
+ )
526
+ end
527
+
528
+ def mlongdiv_value=(value)
529
+ update_temp_order(value, "mlongdiv")
530
+ end
531
+
532
+ def none_value=(_)
533
+ @value&.delete("none")
534
+ end
535
+
536
+ def maligngroup_value=(value)
537
+ @value&.delete("maligngroup")
538
+ end
539
+
540
+ def menclose_value=(value)
541
+ update_temp_order(value, "menclose")
542
+ end
543
+
544
+ def mspace_value=(value)
545
+ return if value.nil? || value.empty?
546
+
547
+ if value.first.linebreak
548
+ linebreak = Math::Function::Linebreak.new(
549
+ nil,
550
+ { linebreak: value.first.linebreak }
551
+ )
552
+ update(
553
+ replace_order_with_value(
554
+ @value,
555
+ [linebreak],
556
+ "mspace"
557
+ )
558
+ )
559
+ else
560
+ @value&.delete("mspace")
561
+ end
562
+ end
563
+
564
+ def malignmark_value=(value)
565
+ @value&.delete("malignmark")
566
+ end
567
+
568
+ def mpadded_value=(value)
569
+ update_temp_order(value, "mpadded")
570
+ end
571
+
572
+ def mfraction_value=(value)
573
+ update_temp_order(value, "mfraction")
574
+ end
575
+
576
+ def mmultiscripts_value=(value)
577
+ update_temp_order(value, "mmultiscripts")
578
+ end
579
+
580
+ def mphantom_value=(value)
581
+ update_temp_order(value, "mphantom")
582
+ end
583
+
584
+ def mglyph_value=(value)
585
+ update_temp_order(value, "mglyph")
586
+ end
587
+
307
588
  protected
308
589
 
590
+ def update_temp_order(value, order_name)
591
+ return if value.nil? || value.empty?
592
+
593
+ update(
594
+ replace_order_with_value(
595
+ Array(@value),
596
+ Array(update_temp_mathml_values(value)),
597
+ order_name
598
+ )
599
+ )
600
+ end
601
+
602
+ def remove_order(order)
603
+ value.delete_if { |val| val.is_a?(String) && val == order }
604
+ end
605
+
309
606
  def boolean_display_style(display_style = displaystyle)
310
607
  YAML.safe_load(display_style.to_s)
311
608
  end
@@ -561,7 +858,7 @@ module Plurimath
561
858
  mrow_nodes << nodes.delete_at(1)
562
859
  next
563
860
  when "mrow"
564
- second_arg = mrow_nodes.map { |node| encode(node.nodes.first) }.join
861
+ second_arg = mrow_nodes.map { |n| encode(n.nodes.first) }.join
565
862
  third_arg = upcase_dd_intent_name(node.nodes[1..-2])
566
863
  mrow_nodes << nodes.delete_at(1)
567
864
  break
@@ -699,6 +996,12 @@ module Plurimath
699
996
  encode(str)
700
997
  end
701
998
  # Dd derivative nodes end
999
+
1000
+ def type_error!(type)
1001
+ raise Math::InvalidTypeError.new(
1002
+ "Invalid type provided: #{type}. Must be one of #{MATH_ZONE_TYPES.join(', ')}.",
1003
+ )
1004
+ end
702
1005
  end
703
1006
  end
704
1007
  end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "binary_function"
4
+ require_relative "../../mathml/utility"
4
5
 
5
6
  module Plurimath
6
7
  module Math
7
8
  module Function
8
9
  class Base < BinaryFunction
10
+ include Mathml::Utility
11
+
9
12
  attr_accessor :options
13
+
10
14
  FUNCTION = {
11
15
  name: "subscript",
12
16
  first_value: "base",
@@ -7,6 +7,7 @@ module Plurimath
7
7
  module Function
8
8
  class Color < BinaryFunction
9
9
  attr_accessor :options
10
+
10
11
  FUNCTION = {
11
12
  name: "color",
12
13
  first_value: "mathcolor",
@@ -27,11 +28,10 @@ module Plurimath
27
28
  end
28
29
 
29
30
  def to_mathml_without_math_tag(intent, options:)
30
- color_value = parameter_one&.to_asciimath(options: options)&.gsub(/\s/, "")&.gsub(/"/, "")
31
31
  Utility.update_nodes(
32
32
  Utility.ox_element(
33
33
  "mstyle",
34
- attributes: { attr_key => color_value },
34
+ attributes: mathml_options,
35
35
  ),
36
36
  [parameter_two&.to_mathml_without_math_tag(intent, options: options)],
37
37
  )
@@ -56,13 +56,26 @@ module Plurimath
56
56
  end
57
57
 
58
58
  def to_unicodemath(options:)
59
- "(#{parameter_one.to_unicodemath(options: options)}&#{parameter_two.to_unicodemath(options: options)})"
59
+ "#{color_symbol}(#{parameter_one.to_unicodemath(options: options)}&#{parameter_two.to_unicodemath(options: options)})"
60
60
  end
61
61
 
62
62
  protected
63
63
 
64
+ def color_symbol
65
+ options&.dig(:backgroundcolor) ? "☁" : "✎"
66
+ end
67
+
64
68
  def attr_key
65
- (options && options[:backcolor]) ? :mathbackground : :mathcolor
69
+ options&.dig(:backgroundcolor) ? :mathbackground : :mathcolor
70
+ end
71
+
72
+ def mathml_options
73
+ return unless parameter_one
74
+
75
+ color_options = {}
76
+ color_options[attr_key] =
77
+ parameter_one.to_asciimath(options: options)&.gsub(/\s/, "")&.gsub(/"/, "")
78
+ color_options
66
79
  end
67
80
  end
68
81
  end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "ternary_function"
4
+ require_relative "../../mathml/utility"
4
5
 
5
6
  module Plurimath
6
7
  module Math
7
8
  module Function
8
9
  class Fenced < TernaryFunction
10
+ include Mathml::Utility
11
+
9
12
  attr_accessor :options
10
13
 
11
14
  def initialize(
@@ -157,8 +160,220 @@ module Plurimath
157
160
  }
158
161
  end
159
162
 
163
+ def separators=(value)
164
+ return if value.nil?
165
+
166
+ @options[:separators] = value
167
+ end
168
+
169
+ def content=(value)
170
+ if parens_nil?
171
+ @parameter_one = Math::Symbols::Paren::Lround.new
172
+ @parameter_three = Math::Symbols::Paren::Rround.new
173
+ end
174
+ end
175
+
176
+ def open=(value)
177
+ return unless value
178
+
179
+ @parameter_one = validate_symbols(value)
180
+ end
181
+
182
+ def close=(value)
183
+ return unless value
184
+
185
+ @parameter_three = validate_symbols(value)
186
+ end
187
+
188
+ def element_order=(value)
189
+ @parameter_two = validated_order(value)
190
+ end
191
+
192
+ def mi_value=(value)
193
+ return if value.nil? || value.empty?
194
+
195
+ update(
196
+ replace_order_with_value(
197
+ @parameter_two,
198
+ Array(validate_symbols(value)),
199
+ "mi"
200
+ )
201
+ )
202
+ end
203
+
204
+ def mn_value=(value)
205
+ return if value.nil? || value.empty?
206
+
207
+ update(
208
+ replace_order_with_value(
209
+ @parameter_two,
210
+ Array(validate_symbols(value)),
211
+ "mn"
212
+ )
213
+ )
214
+ end
215
+
216
+ def mtext_value=(value)
217
+ return if value.nil? || value.empty?
218
+
219
+ update(
220
+ replace_order_with_value(
221
+ @parameter_two,
222
+ Array(validate_symbols(value)),
223
+ "mtext"
224
+ )
225
+ )
226
+ end
227
+
228
+ def mo_value=(value)
229
+ return if value.nil? || value.empty?
230
+
231
+ update(
232
+ replace_order_with_value(
233
+ @parameter_two,
234
+ Array(validate_symbols(value)),
235
+ "mo"
236
+ )
237
+ )
238
+ end
239
+
240
+ def mstyle_value=(value)
241
+ return if value.empty?
242
+
243
+ update(
244
+ replace_order_with_value(
245
+ @parameter_two,
246
+ Array(value),
247
+ "mstyle"
248
+ )
249
+ )
250
+ end
251
+
252
+ def munderover_value=(value)
253
+ update_temp_order(value, "munderover")
254
+ end
255
+
256
+ def msub_value=(value)
257
+ update_temp_order(value, "msub")
258
+ end
259
+
260
+ def msup_value=(value)
261
+ update_temp_order(value, "msup")
262
+ end
263
+
264
+ def mover_value=(value)
265
+ update_temp_order(value, "mover")
266
+ end
267
+
268
+ def munder_value=(value)
269
+ update_temp_order(value, "munder")
270
+ end
271
+
272
+ def msubsup_value=(value)
273
+ update_temp_order(value, "msubsup")
274
+ end
275
+
276
+ def mfrac_value=(value)
277
+ update_temp_order(value, "mfrac")
278
+ end
279
+
280
+ def msqrt_value=(value)
281
+ update_temp_order(value, "msqrt")
282
+ end
283
+
284
+ def mfenced_value=(value)
285
+ update_temp_order(value, "mfenced")
286
+ end
287
+
288
+ def mtable_value=(value)
289
+ return if value.nil? || value.empty?
290
+
291
+ update(
292
+ replace_order_with_value(
293
+ Array(@parameter_two),
294
+ update_temp_mathml_values(value),
295
+ "mtable"
296
+ )
297
+ )
298
+ end
299
+
300
+ def mrow_value=(value)
301
+ return if value.nil? || value.empty?
302
+
303
+ update(
304
+ replace_order_with_value(
305
+ Array(@parameter_two),
306
+ update_temp_mathml_values(
307
+ Array(filter_values(value, array_to_instance: true))
308
+ ),
309
+ "mrow"
310
+ )
311
+ )
312
+ end
313
+
314
+ def mspace_value=(value)
315
+ return if value.nil? || value.empty?
316
+
317
+ if value.first.linebreak
318
+ linebreak = Math::Function::Linebreak.new(
319
+ nil,
320
+ { linebreak: value.first.linebreak }
321
+ )
322
+
323
+ update(
324
+ replace_order_with_value(
325
+ Array(@parameter_two),
326
+ [linebreak],
327
+ "mspace"
328
+ )
329
+ )
330
+ else
331
+ @parameter_two&.delete("mspace")
332
+ end
333
+ end
334
+
335
+ def mpadded_value=(value)
336
+ update_temp_order(value, "mpadded")
337
+ end
338
+
339
+ def mfraction_value=(value)
340
+ update_temp_order(value, "mfraction")
341
+ end
342
+
343
+ def mmultiscripts_value=(value)
344
+ update_temp_order(value, "mmultiscripts")
345
+ end
346
+
347
+ def mphantom_value=(value)
348
+ update_temp_order(value, "mphantom")
349
+ end
350
+
160
351
  protected
161
352
 
353
+ def update_temp_order(value, order_name)
354
+ return if value.nil? || value.empty?
355
+
356
+ update(
357
+ replace_order_with_value(
358
+ Array(@parameter_two),
359
+ update_temp_mathml_values(value),
360
+ order_name
361
+ )
362
+ )
363
+ end
364
+
365
+ def remove_order(order)
366
+ @parameter_two.delete_if { |val| val.is_a?(String) && val == order }
367
+ end
368
+
369
+ def insert(values)
370
+ update(Array(@parameter_two) + values)
371
+ end
372
+
373
+ def update(object)
374
+ @parameter_two = Array(object)
375
+ end
376
+
162
377
  def open_paren(dpr, options:)
163
378
  first_value = symbol_or_paren(parameter_one, lang: :omml, options: options)
164
379
  return dpr if first_value.nil?
@@ -359,6 +574,10 @@ module Plurimath
359
574
  node["intent"]&.start_with?(":partial-derivative") &&
360
575
  !node.locate("*/@arg").include?("f")
361
576
  end
577
+
578
+ def parens_nil?
579
+ @parameter_one.nil? && @parameter_three.nil?
580
+ end
362
581
  end
363
582
  end
364
583
  end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "binary_function"
4
+ require_relative "../../mathml/utility"
4
5
 
5
6
  module Plurimath
6
7
  module Math
7
8
  module Function
8
9
  class Frac < BinaryFunction
10
+ include Mathml::Utility
11
+
9
12
  attr_accessor :options
13
+
10
14
  FUNCTION = {
11
15
  name: "fraction",
12
16
  first_value: "numerator",
@@ -17,7 +17,7 @@ module Plurimath
17
17
  def ==(object)
18
18
  object.class == self.class &&
19
19
  object.parameter_one == parameter_one &&
20
- object.linebreak == linebreak
20
+ object.linebreak? == linebreak?
21
21
  end
22
22
 
23
23
  def to_asciimath(options:)
@@ -91,7 +91,7 @@ module Plurimath
91
91
  true
92
92
  end
93
93
 
94
- def linebreak
94
+ def linebreak?
95
95
  true
96
96
  end
97
97
  end