plurimath 0.11.0 → 0.11.3

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop_todo.yml +311 -58
  4. data/Gemfile +3 -2
  5. data/README.adoc +331 -15
  6. data/lib/plurimath/asciimath/parse.rb +6 -1
  7. data/lib/plurimath/asciimath/transform.rb +2 -0
  8. data/lib/plurimath/base_number_prefix.rb +43 -0
  9. data/lib/plurimath/configuration.rb +9 -1
  10. data/lib/plurimath/deprecation.rb +2 -1
  11. data/lib/plurimath/errors/configuration_error.rb +5 -1
  12. data/lib/plurimath/errors/deprecation_error.rb +2 -1
  13. data/lib/plurimath/errors/evaluation/division_by_zero_error.rb +13 -0
  14. data/lib/plurimath/errors/evaluation/error.rb +9 -0
  15. data/lib/plurimath/errors/evaluation/invalid_binding_error.rb +14 -0
  16. data/lib/plurimath/errors/evaluation/invalid_binding_key_error.rb +14 -0
  17. data/lib/plurimath/errors/evaluation/math_domain_error.rb +9 -0
  18. data/lib/plurimath/errors/evaluation/missing_variable_error.rb +13 -0
  19. data/lib/plurimath/errors/evaluation/non_finite_result_error.rb +13 -0
  20. data/lib/plurimath/errors/evaluation/unsupported_expression_error.rb +13 -0
  21. data/lib/plurimath/errors/evaluation.rb +18 -0
  22. data/lib/plurimath/errors/invalid_number.rb +17 -0
  23. data/lib/plurimath/errors/unsupported_base.rb +18 -0
  24. data/lib/plurimath/errors/{formatter/unsupported_locale.rb → unsupported_locale.rb} +1 -1
  25. data/lib/plurimath/errors.rb +9 -0
  26. data/lib/plurimath/formatter/numbers/base_notation.rb +55 -27
  27. data/lib/plurimath/formatter/numbers/format_options.rb +123 -17
  28. data/lib/plurimath/formatter/numbers/formatted_notation.rb +62 -0
  29. data/lib/plurimath/formatter/numbers/formatted_number.rb +87 -0
  30. data/lib/plurimath/formatter/numbers/fraction.rb +8 -17
  31. data/lib/plurimath/formatter/numbers/integer.rb +3 -0
  32. data/lib/plurimath/formatter/numbers/mathml_renderer.rb +56 -0
  33. data/lib/plurimath/formatter/numbers/notation_renderer.rb +58 -28
  34. data/lib/plurimath/formatter/numbers/number_renderer.rb +26 -13
  35. data/lib/plurimath/formatter/numbers/omml_renderer.rb +74 -0
  36. data/lib/plurimath/formatter/numbers/precision_resolver.rb +16 -3
  37. data/lib/plurimath/formatter/numbers/sign_renderer.rb +2 -1
  38. data/lib/plurimath/formatter/numbers/significant.rb +4 -2
  39. data/lib/plurimath/formatter/numbers/source.rb +46 -6
  40. data/lib/plurimath/formatter/numbers/text_renderer.rb +52 -0
  41. data/lib/plurimath/formatter/numbers.rb +6 -2
  42. data/lib/plurimath/formatter/standard.rb +6 -0
  43. data/lib/plurimath/formatter/supported_locales.rb +1 -1
  44. data/lib/plurimath/formatter.rb +0 -2
  45. data/lib/plurimath/html/parse.rb +5 -0
  46. data/lib/plurimath/html/transform.rb +26 -12
  47. data/lib/plurimath/latex/parse.rb +5 -0
  48. data/lib/plurimath/latex/transform.rb +2 -0
  49. data/lib/plurimath/math/core.rb +69 -14
  50. data/lib/plurimath/math/evaluation/evaluator.rb +147 -0
  51. data/lib/plurimath/math/evaluation/expression_parser.rb +215 -0
  52. data/lib/plurimath/math/evaluation/iteration.rb +63 -0
  53. data/lib/plurimath/math/evaluation.rb +13 -0
  54. data/lib/plurimath/math/formula.rb +9 -0
  55. data/lib/plurimath/math/function/abs.rb +4 -0
  56. data/lib/plurimath/math/function/arccos.rb +4 -0
  57. data/lib/plurimath/math/function/arcsin.rb +4 -0
  58. data/lib/plurimath/math/function/arctan.rb +4 -0
  59. data/lib/plurimath/math/function/ceil.rb +4 -0
  60. data/lib/plurimath/math/function/cos.rb +4 -0
  61. data/lib/plurimath/math/function/cosh.rb +4 -0
  62. data/lib/plurimath/math/function/cot.rb +4 -0
  63. data/lib/plurimath/math/function/coth.rb +4 -0
  64. data/lib/plurimath/math/function/csc.rb +4 -0
  65. data/lib/plurimath/math/function/csch.rb +4 -0
  66. data/lib/plurimath/math/function/exp.rb +4 -0
  67. data/lib/plurimath/math/function/fenced.rb +4 -0
  68. data/lib/plurimath/math/function/floor.rb +4 -0
  69. data/lib/plurimath/math/function/frac.rb +7 -0
  70. data/lib/plurimath/math/function/gcd.rb +9 -0
  71. data/lib/plurimath/math/function/lcm.rb +9 -0
  72. data/lib/plurimath/math/function/lg.rb +4 -0
  73. data/lib/plurimath/math/function/ln.rb +4 -0
  74. data/lib/plurimath/math/function/log.rb +19 -0
  75. data/lib/plurimath/math/function/max.rb +4 -0
  76. data/lib/plurimath/math/function/min.rb +4 -0
  77. data/lib/plurimath/math/function/mod.rb +15 -0
  78. data/lib/plurimath/math/function/overleftrightarrow.rb +2 -2
  79. data/lib/plurimath/math/function/power.rb +10 -0
  80. data/lib/plurimath/math/function/prod.rb +10 -0
  81. data/lib/plurimath/math/function/root.rb +7 -0
  82. data/lib/plurimath/math/function/sec.rb +4 -0
  83. data/lib/plurimath/math/function/sech.rb +4 -0
  84. data/lib/plurimath/math/function/sin.rb +4 -0
  85. data/lib/plurimath/math/function/sinh.rb +4 -0
  86. data/lib/plurimath/math/function/sqrt.rb +4 -0
  87. data/lib/plurimath/math/function/sum.rb +10 -0
  88. data/lib/plurimath/math/function/tan.rb +4 -0
  89. data/lib/plurimath/math/function/tanh.rb +4 -0
  90. data/lib/plurimath/math/function/text.rb +17 -0
  91. data/lib/plurimath/math/number.rb +44 -23
  92. data/lib/plurimath/math/symbols/cdot.rb +4 -0
  93. data/lib/plurimath/math/symbols/div.rb +4 -0
  94. data/lib/plurimath/math/symbols/hat.rb +4 -0
  95. data/lib/plurimath/math/symbols/minus.rb +4 -0
  96. data/lib/plurimath/math/symbols/pi.rb +5 -1
  97. data/lib/plurimath/math/symbols/plus.rb +4 -0
  98. data/lib/plurimath/math/symbols/slash.rb +4 -0
  99. data/lib/plurimath/math/symbols/symbol.rb +45 -0
  100. data/lib/plurimath/math/symbols/times.rb +4 -0
  101. data/lib/plurimath/math.rb +5 -1
  102. data/lib/plurimath/mathml/constants.rb +18 -0
  103. data/lib/plurimath/number_formatter.rb +63 -3
  104. data/lib/plurimath/omml/formula_transformation.rb +2 -1
  105. data/lib/plurimath/omml/translator.rb +4 -1
  106. data/lib/plurimath/setup/opal.rb.erb +13 -0
  107. data/lib/plurimath/unicode_math/parse.rb +98 -54
  108. data/lib/plurimath/unicode_math/parser.rb +7 -4
  109. data/lib/plurimath/unicode_math/parsing_rules/absence_rules.rb +33 -21
  110. data/lib/plurimath/unicode_math/parsing_rules/common_rules.rb +28 -16
  111. data/lib/plurimath/unicode_math/parsing_rules/constants_rules.rb +18 -7
  112. data/lib/plurimath/unicode_math/parsing_rules/masked.rb +35 -15
  113. data/lib/plurimath/unicode_math/parsing_rules/sub_sup.rb +98 -72
  114. data/lib/plurimath/unicode_math/transform.rb +1 -0
  115. data/lib/plurimath/utility.rb +1 -1
  116. data/lib/plurimath/version.rb +1 -1
  117. data/lib/plurimath.rb +2 -0
  118. metadata +24 -5
  119. data/lib/plurimath/errors/formatter/unsupported_base.rb +0 -21
  120. data/lib/plurimath/formatter/numbers/parts_renderer.rb +0 -30
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ module Math
5
+ module Evaluation
6
+ # Evaluates bounded Sum/Prod iterations: parses the `i=1` lower bound,
7
+ # validates the integer bounds and step limit, then folds the body with
8
+ # the index temporarily bound.
9
+ class Iteration
10
+ def initialize(evaluator, lower, upper, body)
11
+ @evaluator = evaluator
12
+ @lower = lower
13
+ @upper = upper
14
+ @body = body
15
+ end
16
+
17
+ def accumulate(initial, operation)
18
+ name, from = index_definition
19
+ to = evaluator.evaluate_node(upper)
20
+ validate_bounds(from, to)
21
+
22
+ (from..to).reduce(initial) do |accumulator, index|
23
+ accumulator.public_send(operation, body_value(name, index))
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :evaluator, :lower, :upper, :body
30
+
31
+ # Parses `i=1` style lower bounds into the index name and its start.
32
+ def index_definition
33
+ tokens = lower.is_a?(Formula) ? lower.value : Array(lower)
34
+ index = tokens.first
35
+ if index.is_a?(Core) && index.reserved_constant
36
+ evaluator.unsupported("reserved constant as iteration index")
37
+ end
38
+ name = index.is_a?(Core) ? index.variable_name : nil
39
+ unless name && tokens[1].is_a?(Symbols::Equal)
40
+ evaluator.unsupported("malformed iteration bounds")
41
+ end
42
+
43
+ [name, evaluator.evaluate_nodes(tokens[2..])]
44
+ end
45
+
46
+ def validate_bounds(from, to)
47
+ unless from.is_a?(Integer) && to.is_a?(Integer)
48
+ raise Errors::Evaluation::MathDomainError, "iteration bounds must be integers"
49
+ end
50
+
51
+ limit = Plurimath.configuration.evaluation_max_iterations
52
+ return if limit.nil? || (to - from + 1) <= limit
53
+
54
+ evaluator.unsupported("iteration range larger than #{limit} steps")
55
+ end
56
+
57
+ def body_value(name, index)
58
+ evaluator.with_binding(name, index) { evaluator.evaluate_node(body) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plurimath/errors/evaluation"
4
+
5
+ module Plurimath
6
+ module Math
7
+ module Evaluation
8
+ autoload :Evaluator, "#{__dir__}/evaluation/evaluator"
9
+ autoload :ExpressionParser, "#{__dir__}/evaluation/expression_parser"
10
+ autoload :Iteration, "#{__dir__}/evaluation/iteration"
11
+ end
12
+ end
13
+ end
@@ -54,6 +54,15 @@ module Plurimath
54
54
  object.left_right_wrapper == left_right_wrapper
55
55
  end
56
56
 
57
+ def evaluate(bindings = {})
58
+ hash = bindings.to_hash if bindings.respond_to?(:to_hash)
59
+ unless hash.is_a?(Hash)
60
+ raise ArgumentError, "bindings must be a Hash-like object"
61
+ end
62
+
63
+ Evaluation::Evaluator.new(self, hash).evaluate
64
+ end
65
+
57
66
  def to_asciimath(formatter: nil, unitsml: {}, options: nil)
58
67
  options ||= { formatter: formatter, unitsml: unitsml }.compact
59
68
  options[:formula] ||= self
@@ -39,6 +39,10 @@ module Plurimath
39
39
  "⒜#{unicodemath_parens(parameter_one, options: options)}"
40
40
  end
41
41
 
42
+ def evaluate(evaluator)
43
+ evaluator.evaluate_node(parameter_one).abs
44
+ end
45
+
42
46
  def intent_names
43
47
  { name: "absolute-value" }
44
48
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.acos(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.asin(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.atan(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -39,6 +39,10 @@ module Plurimath
39
39
  "#{first_value}#{parameter_one&.to_unicodemath(options: options)}#{second_value}"
40
40
  end
41
41
 
42
+ def evaluate(evaluator)
43
+ evaluator.evaluate_node(parameter_one).ceil
44
+ end
45
+
42
46
  def line_breaking(obj)
43
47
  parameter_one.line_breaking(obj)
44
48
  if obj.value_exist?
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.cos(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.cosh(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ evaluator.divide(1.0, ::Math.tan(evaluator.evaluate_node(parameter_one)))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ evaluator.divide(1.0, ::Math.tanh(evaluator.evaluate_node(parameter_one)))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ evaluator.divide(1.0, ::Math.sin(evaluator.evaluate_node(parameter_one)))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ evaluator.divide(1.0, ::Math.sinh(evaluator.evaluate_node(parameter_one)))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -8,6 +8,10 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ ::Math.exp(evaluator.evaluate_node(parameter_one))
13
+ end
14
+
11
15
  def to_omml_without_math_tag(display_style, options:)
12
16
  array = []
13
17
  array << r_element("exp", rpr_tag: false) unless hide_function_name
@@ -116,6 +116,10 @@ module Plurimath
116
116
  "#{unicode_open_paren(options: options)}#{fenced_value}#{unicode_close_paren(options: options)}"
117
117
  end
118
118
 
119
+ def evaluate(evaluator)
120
+ evaluator.evaluate_nodes(parameter_two)
121
+ end
122
+
119
123
  def to_asciimath_math_zone(spacing, last = false, indent = true,
120
124
  options:)
121
125
  filtered_values(parameter_two,
@@ -36,6 +36,10 @@ module Plurimath
36
36
  "#{first_value}#{parameter_one&.to_unicodemath(options: options)}#{second_value}"
37
37
  end
38
38
 
39
+ def evaluate(evaluator)
40
+ evaluator.evaluate_node(parameter_one).floor
41
+ end
42
+
39
43
  def line_breaking(obj)
40
44
  parameter_one.line_breaking(obj)
41
45
  if obj.value_exist?
@@ -89,6 +89,13 @@ module Plurimath
89
89
  "#{first_value}∕#{second_value}" if self.options&.key?(:ldiv)
90
90
  end
91
91
 
92
+ def evaluate(evaluator)
93
+ evaluator.divide(
94
+ evaluator.evaluate_node(parameter_one),
95
+ evaluator.evaluate_node(parameter_two),
96
+ )
97
+ end
98
+
92
99
  def line_breaking(obj)
93
100
  parameter_one&.line_breaking(obj)
94
101
  if obj.value_exist?
@@ -8,6 +8,15 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ values = evaluator.function_arguments(parameter_one)
13
+ unless values.all?(Integer)
14
+ raise Errors::Evaluation::MathDomainError, "gcd requires integer arguments"
15
+ end
16
+
17
+ values.reduce(:gcd)
18
+ end
19
+
11
20
  def to_omml_without_math_tag(display_style, options:)
12
21
  array = []
13
22
  array << r_element("gcd", rpr_tag: false) unless hide_function_name
@@ -8,6 +8,15 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ values = evaluator.function_arguments(parameter_one)
13
+ unless values.all?(Integer)
14
+ raise Errors::Evaluation::MathDomainError, "lcm requires integer arguments"
15
+ end
16
+
17
+ values.reduce(:lcm)
18
+ end
19
+
11
20
  def to_asciimath(options:)
12
21
  first_value = " #{asciimath_value(options: options)}" if parameter_one
13
22
  "lcm#{first_value}"
@@ -8,6 +8,10 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ ::Math.log10(evaluator.evaluate_node(parameter_one))
13
+ end
14
+
11
15
  def to_omml_without_math_tag(display_style, options:)
12
16
  array = []
13
17
  array << r_element("lg", rpr_tag: false) unless hide_function_name
@@ -8,6 +8,10 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ ::Math.log(evaluator.evaluate_node(parameter_one))
13
+ end
14
+
11
15
  def to_omml_without_math_tag(display_style, options:)
12
16
  array = []
13
17
  array = r_element("ln", rpr_tag: false) unless hide_function_name
@@ -10,6 +10,25 @@ module Plurimath
10
10
  second_value: "supscript",
11
11
  }.freeze
12
12
 
13
+ # `log` carries its base/exponent but not its argument; the parser
14
+ # binds the following fenced group via #evaluate_with_argument, so a
15
+ # bare `log` is unsupported.
16
+ def evaluate(evaluator)
17
+ evaluator.unsupported(self)
18
+ end
19
+
20
+ def evaluate_with_argument(evaluator, argument)
21
+ base = parameter_one ? evaluator.evaluate_node(parameter_one) : 10
22
+ unless base.positive? && base != 1
23
+ raise Errors::Evaluation::MathDomainError,
24
+ "log base must be a positive number other than 1"
25
+ end
26
+
27
+ result = ::Math.log(evaluator.evaluate_node(argument), base)
28
+ result = evaluator.power(result, evaluator.evaluate_node(parameter_two)) if parameter_two
29
+ result
30
+ end
31
+
13
32
  def to_asciimath(options:)
14
33
  if parameter_one
15
34
  first_value = "_#{wrapped(parameter_one,
@@ -8,6 +8,10 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ evaluator.function_arguments(parameter_one).max
13
+ end
14
+
11
15
  def to_omml_without_math_tag(display_style, options:)
12
16
  array = []
13
17
  array << r_element("max", rpr_tag: false) unless hide_function_name
@@ -8,6 +8,10 @@ module Plurimath
8
8
  false
9
9
  end
10
10
 
11
+ def evaluate(evaluator)
12
+ evaluator.function_arguments(parameter_one).min
13
+ end
14
+
11
15
  def to_omml_without_math_tag(display_style, options:)
12
16
  array = []
13
17
  array << r_element("min", rpr_tag: false) unless hide_function_name
@@ -10,6 +10,21 @@ module Plurimath
10
10
  second_value: "argument",
11
11
  }.freeze
12
12
 
13
+ def evaluate(evaluator)
14
+ evaluator.modulo(
15
+ evaluator.evaluate_node(parameter_one),
16
+ evaluator.evaluate_node(parameter_two),
17
+ )
18
+ end
19
+
20
+ # `-a mod b` negates the dividend, matching `(-a) mod b`.
21
+ def evaluate_negated(evaluator)
22
+ evaluator.modulo(
23
+ -evaluator.evaluate_node(parameter_one),
24
+ evaluator.evaluate_node(parameter_two),
25
+ )
26
+ end
27
+
13
28
  def to_asciimath(options:)
14
29
  first_value = parameter_one&.to_asciimath(options: options)
15
30
  second_value = parameter_two&.to_asciimath(options: options)
@@ -14,7 +14,7 @@ module Plurimath
14
14
  def to_mathml_without_math_tag(intent, options:)
15
15
  mover = ox_element("mover")
16
16
  first_value = parameter_one&.to_mathml_without_math_tag(intent,
17
- options: options)
17
+ options: options)
18
18
  if attributes && attributes[:accent]
19
19
  mover[:accent] =
20
20
  attributes[:accent]
@@ -36,7 +36,7 @@ module Plurimath
36
36
  else
37
37
  symbol = Symbols::Symbol.new("\u20e1")
38
38
  Overset.new(parameter_one, symbol).to_omml_without_math_tag(true,
39
- options: options)
39
+ options: options)
40
40
  end
41
41
  end
42
42
 
@@ -80,6 +80,16 @@ module Plurimath
80
80
  end
81
81
  end
82
82
 
83
+ # Nested Power trees evaluate literally: `Power(Power(2, 3), 2)` is
84
+ # `(2^3)^2`, whether it came from explicit MathML nesting or a
85
+ # left-nested source chain.
86
+ def evaluate(evaluator)
87
+ evaluator.power(
88
+ evaluator.evaluate_node(parameter_one),
89
+ evaluator.evaluate_node(parameter_two),
90
+ )
91
+ end
92
+
83
93
  def line_breaking(obj)
84
94
  parameter_one&.line_breaking(obj)
85
95
  if obj.value_exist?
@@ -12,6 +12,16 @@ module Plurimath
12
12
  second_value: "supscript",
13
13
  }.freeze
14
14
 
15
+ def evaluate(evaluator)
16
+ if parameter_one.nil? || parameter_two.nil? || parameter_three.nil?
17
+ evaluator.unsupported(self)
18
+ end
19
+
20
+ Evaluation::Iteration
21
+ .new(evaluator, parameter_one, parameter_two, parameter_three)
22
+ .accumulate(1, :*)
23
+ end
24
+
15
25
  def initialize(parameter_one = nil,
16
26
  parameter_two = nil,
17
27
  parameter_three = nil,
@@ -51,6 +51,13 @@ module Plurimath
51
51
  second_value = parameter_two.to_unicodemath(options: options) if parameter_two
52
52
  "√(#{first_value}&#{second_value})"
53
53
  end
54
+
55
+ def evaluate(evaluator)
56
+ evaluator.power(
57
+ evaluator.evaluate_node(parameter_two),
58
+ evaluator.divide(1.0, evaluator.evaluate_node(parameter_one)),
59
+ )
60
+ end
54
61
  end
55
62
  end
56
63
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ evaluator.divide(1.0, ::Math.cos(evaluator.evaluate_node(parameter_one)))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ evaluator.divide(1.0, ::Math.cosh(evaluator.evaluate_node(parameter_one)))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.sin(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.sinh(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -42,6 +42,10 @@ module Plurimath
42
42
  def to_unicodemath(options:)
43
43
  "√#{unicodemath_parens(parameter_one, options: options)}"
44
44
  end
45
+
46
+ def evaluate(evaluator)
47
+ ::Math.sqrt(evaluator.evaluate_node(parameter_one))
48
+ end
45
49
  end
46
50
  end
47
51
  end
@@ -13,6 +13,16 @@ module Plurimath
13
13
  third_value: "term",
14
14
  }.freeze
15
15
 
16
+ def evaluate(evaluator)
17
+ if parameter_one.nil? || parameter_two.nil? || parameter_three.nil?
18
+ evaluator.unsupported(self)
19
+ end
20
+
21
+ Evaluation::Iteration
22
+ .new(evaluator, parameter_one, parameter_two, parameter_three)
23
+ .accumulate(0, :+)
24
+ end
25
+
16
26
  def initialize(parameter_one = nil,
17
27
  parameter_two = nil,
18
28
  parameter_three = nil,
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.tan(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -7,6 +7,10 @@ module Plurimath
7
7
  def validate_function_formula
8
8
  false
9
9
  end
10
+
11
+ def evaluate(evaluator)
12
+ ::Math.tanh(evaluator.evaluate_node(parameter_one))
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -66,6 +66,23 @@ module Plurimath
66
66
  false
67
67
  end
68
68
 
69
+ # OMML emits variable text runs as Text nodes. Only a plain, non-blank
70
+ # string names a variable; rich content (arrays/formulas) or a missing
71
+ # value does not.
72
+ def variable_name
73
+ return unless parameter_one.is_a?(String)
74
+
75
+ stripped = parameter_one.strip
76
+ stripped unless stripped.empty?
77
+ end
78
+
79
+ def evaluate(evaluator)
80
+ name = variable_name
81
+ evaluator.unsupported(self) unless name
82
+
83
+ evaluator.value_for(name)
84
+ end
85
+
69
86
  def to_asciimath_math_zone(spacing, _, _, options:)
70
87
  "#{spacing}#{to_asciimath(options: options)} text\n"
71
88
  end