plurimath 0.8.1 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43bfa34fd25956b0ee8308569e2f42f83f25a714e1d1f736d0b39f9fb464a312
4
- data.tar.gz: df1e211c96baf094adb4d5fdb784b24d47750ac15c00ddfcd4ca9565f8d2e916
3
+ metadata.gz: def00e41e6010e06ea7df51573e342c4ed8b815e079bcbad3a7776cc5d625e02
4
+ data.tar.gz: d46595b7a57d2a391e849d58a6b23f7e449e042e891797967f13cee0364c32df
5
5
  SHA512:
6
- metadata.gz: 6119f28efdfb00ad31b7fa32bab9d21b95c50c92a24be7610988677cb9708debcc9a8fd71d1896cea6595f9fe2ca1e7b5c1e33ca109aa9577b9bd1d246fc8cc3
7
- data.tar.gz: 864c82b97e142080d9a5f2524e3879d0102721adb5788dbadfa1baaa3e7c89ea433c395e1b43d447556a72ae078b23b43d7dbb24d0032d9a19d2844bbfefc75b
6
+ metadata.gz: 5e5c0c02a41fcd2fdbebbd11ca103228ec5c9a2b4e68e98a364c39d988540ebfa844ad06785ced1538186ae7a0583978fe8c1a548c1da135d243d633557cc298
7
+ data.tar.gz: 15463a536fac275fe0935b360dd754ed787a202d4d77b4ef3f4934359e45ec4393e0c95bd6a7ce7f02a9e655f21eeeec312f932462354965a287e08e92b55e89
@@ -896,6 +896,11 @@ module Plurimath
896
896
  Math::Formula.new([table] + expr.flatten.compact)
897
897
  end
898
898
 
899
+ rule(table: simple(:table),
900
+ left_right: simple(:left_right)) do
901
+ Math::Formula.new([table, left_right])
902
+ end
903
+
899
904
  rule(table: simple(:table),
900
905
  expr: simple(:expr)) do
901
906
  formula_array = [table]
@@ -0,0 +1,114 @@
1
+ require "twitter_cldr"
2
+ require_relative "twitter_cldr_rb/integer"
3
+ require_relative "twitter_cldr_rb/fraction"
4
+ require_relative "twitter_cldr_rb/number_formatter"
5
+
6
+ module Plurimath
7
+ module Formatter
8
+ class NumberFormatter
9
+ attr_accessor :locale, :localize_number, :localizer_symbols
10
+
11
+ LOCALIZE_NUMBER_REGEX = %r{(?<group>[^#])?(?<groupdigits>#+0)(?<decimal>.)(?<fractdigits>#+)(?<fractgroup>[^#])?}
12
+ SUPPORTED_NOTATIONS = %i[e scientific engineering].freeze
13
+
14
+ def initialize(locale, localize_number:, localizer_symbols:)
15
+ @locale = locale
16
+ @localize_number = localize_number
17
+ @localizer_symbols = localizer_symbols
18
+ @twitter_cldr_reader = twitter_cldr_reader(locale)
19
+ end
20
+
21
+ def localized_number(number_string, locale:, precision:, format:)
22
+ options_instance_variables(number_string, format, precision)
23
+ @twitter_cldr_reader.merge!(format)
24
+ return send("#{@notation}_format", number_string) if SUPPORTED_NOTATIONS.include?(@notation&.to_sym)
25
+
26
+ localize_number(number_string)
27
+ end
28
+
29
+ private
30
+
31
+ def twitter_cldr_reader(locale)
32
+ num = TwitterCldr::DataReaders::NumberDataReader.new(locale)
33
+ num.symbols
34
+ .merge!(@localizer_symbols)
35
+ .merge!(parse_localize_number)
36
+ end
37
+
38
+ def parse_localize_number
39
+ @localize_number or return {}
40
+ m = LOCALIZE_NUMBER_REGEX.match(@localize_number) or return {}
41
+ ret = {
42
+ decimal: m[:decimal],
43
+ group_digits: m[:groupdigits].size,
44
+ fraction_group_digits: m[:fractdigits].size,
45
+ group: m[:group] == " " ? "\u00A0" : (m[:group] || ""),
46
+ fraction_group: m[:fractgroup] == " " ? "\u00A0" : (m[:fractgroup] || "")
47
+ }.compact
48
+ end
49
+
50
+ def localize_number(num)
51
+ num = num.match?(/\./) ? num.to_f : num.to_i
52
+ localized = BigDecimal(num.to_s).localize(@locale)
53
+ return localized.to_s if @precision.zero?
54
+
55
+ localized.to_decimal.to_s(precision: @precision)
56
+ end
57
+
58
+ def e_format(num_str)
59
+ notations_formatting(num_str).join(@e.to_s)
60
+ end
61
+
62
+ def scientific_format(num_str)
63
+ notations_formatting(num_str).join(" #{@times} 10^")
64
+ end
65
+
66
+ def engineering_format(num_str)
67
+ @precision = num_str.length - 1 unless @precision > 0
68
+ chars = notation_chars(num_str)
69
+ update_string_index(chars, chars.last.to_i % 3)
70
+ chars[0] = localize_number(chars[0])
71
+ chars.join(" #{@times} 10^")
72
+ end
73
+
74
+ def update_exponent_sign(str)
75
+ str.gsub!("e", "e+") if @exponent_sign == :plus
76
+ end
77
+
78
+ def notation_chars(num_str)
79
+ notation_number = ("%.#{@precision}e" %num_str)
80
+ update_exponent_sign(notation_number.gsub!("+0", ""))
81
+ notation_number.split("e")
82
+ end
83
+
84
+ def notations_formatting(num_str)
85
+ chars = notation_chars(num_str)
86
+ chars[0] = localize_number(chars[0])
87
+ chars
88
+ end
89
+
90
+ def options_instance_variables(string, format, precision)
91
+ @e = format.delete(:e)&.to_sym || :e
92
+ @times = format.delete(:times)&.to_sym || "\u{d7}"
93
+ @notation = format.delete(:notation)&.to_sym || nil
94
+ @precision = update_precision(string, precision)
95
+ @exponent_sign = format.delete(:exponent_sign)&.to_sym || nil
96
+ end
97
+
98
+ def update_precision(num, precision)
99
+ return precision if precision
100
+ return num.sub(/\./, "").size - 1 if SUPPORTED_NOTATIONS.include?(@notation&.to_sym)
101
+
102
+ /\./.match?(num) ? num.sub(/^.*\./, "").size : 0
103
+ end
104
+
105
+ def update_string_index(chars, index)
106
+ return if index.zero?
107
+
108
+ chars.first.delete!(".")
109
+ chars.first.insert(index + 1, ".")
110
+ chars[-1] = (chars[-1].to_i - index).to_s
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TwitterCldr
4
+ module Formatters
5
+ module Numbers
6
+ class Fraction
7
+ attr_reader :format, :decimal, :precision, :separator, :group
8
+
9
+ def initialize(token, symbols = {})
10
+ @format = token ? token.value.split('.').pop : ''
11
+ @decimal = symbols[:decimal] || '.'
12
+ @separator = symbols[:fraction_group] || " "
13
+ @group = symbols[:fraction_group_digits] || 3
14
+ @digit_count = symbols[:digit_count] || nil
15
+ @precision = @format.length
16
+ end
17
+
18
+ def apply(fraction, options = {}, int = "")
19
+ precision = options[:precision] || self.precision
20
+ return "" unless precision > 0
21
+
22
+ number = interpolate(format(options), fraction, :left)
23
+ number = digit_count_format(int, fraction, number) if @digit_count
24
+ decimal + change_format(precision, number)
25
+ end
26
+
27
+ def format(options)
28
+ precision = options[:precision] || self.precision
29
+ precision ? '0' * precision : @format
30
+ end
31
+
32
+ protected
33
+
34
+ def change_format(precision, string)
35
+ tokens = []
36
+ tokens << string&.slice!(0, group) until string&.empty?
37
+ tokens.compact.join(separator)
38
+ end
39
+
40
+ def digit_count_format(int, fraction, number)
41
+ integer = int + "." + fraction
42
+ float = integer.to_f
43
+ int_length = integer.length - 1
44
+ @digit_count ||= int_length
45
+ if int_length > @digit_count
46
+ float.round(@digit_count - int.length).to_s.split(".").last
47
+ elsif int_length < @digit_count
48
+ number += "0" * (@digit_count - int_length)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TwitterCldr
4
+ module Formatters
5
+ module Numbers
6
+ class Integer
7
+ def initialize(token, symbols = {})
8
+ format = token.value.split('.')[0]
9
+ @format = prepare_format(format, symbols)
10
+ @groups = Array(symbols[:group_digits] || parse_groups(format))
11
+ @separator = symbols[:group] || ','
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TwitterCldr
4
+ module Formatters
5
+ class NumberFormatter
6
+ def format(tokens, number, options = {})
7
+ options[:precision] ||= precision_from(number)
8
+ options[:type] ||= :decimal
9
+
10
+ prefix, suffix, integer_format, fraction_format = *partition_tokens(tokens)
11
+ number = truncate_number(number, integer_format.format.length)
12
+
13
+ int, fraction = parse_number(number, options)
14
+ result = integer_format.apply(int, options)
15
+ result << fraction_format.apply(fraction, options, int) if fraction
16
+
17
+ number_system.transliterate(
18
+ "#{prefix.to_s}#{result}#{suffix.to_s}"
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -288,26 +288,19 @@ module Plurimath
288
288
 
289
289
  def unitsml_post_processing(nodes)
290
290
  nodes.each.with_index do |node, index|
291
- if node.is_a?(Ox::Element) && node.attributes&.dig(:unitsml)
292
- previous = nodes[index-1]
293
- if previous && ["mi", "mn"].include?(previous.name)
294
- if text_in_tag?(node.nodes)
295
- nodes.insert(index, space_element(attributes: true))
296
- else
297
- nodes.insert(index, space_element)
298
- end
299
- end
300
-
301
- node.attributes.delete_if {|k, v| k == :unitsml }
291
+ if node.is_a?(Ox::Element) && node&.attributes&.dig(:unitsml)
292
+ pre_index = index - 1
293
+ pre_node = nodes[pre_index] if pre_index.zero? || pre_index.positive?
294
+ nodes.insert(index, space_element(node)) if valid_previous?(pre_node)
295
+ node.attributes.delete_if { |k, _| k == :unitsml }
302
296
  end
303
-
304
- unitsml_post_processing(node.nodes) if !node.nodes.any?(String)
297
+ unitsml_post_processing(node.nodes) if node.nodes.none?(String)
305
298
  end
306
299
  end
307
300
 
308
- def space_element(attributes: false)
301
+ def space_element(node)
309
302
  element = (ox_element("mo") << "&#x2062;")
310
- element.attributes[:rspace] = "thickmathspace" if attributes
303
+ element.attributes[:rspace] = "thickmathspace" if text_in_tag?(node.nodes)
311
304
  element
312
305
  end
313
306
 
@@ -327,6 +320,17 @@ module Plurimath
327
320
  def unicodemath_value
328
321
  (negated_value? || mini_sized?) ? value&.map(&:to_unicodemath)&.join : value&.map(&:to_unicodemath)&.join(" ")
329
322
  end
323
+
324
+ def valid_previous?(previous)
325
+ return unless previous
326
+
327
+ ["mi", "mn"].include?(previous.name) ||
328
+ inside_tag?(previous)
329
+ end
330
+
331
+ def inside_tag?(previous)
332
+ previous&.nodes&.any? { |node| valid_previous?(node) if node.is_a?(Ox::Element) }
333
+ end
330
334
  end
331
335
  end
332
336
  end
@@ -7,6 +7,7 @@ require_relative "mathml"
7
7
  require_relative "html"
8
8
  require_relative "latex"
9
9
  require_relative "unitsml"
10
+ require_relative "number_formatter"
10
11
  require_relative "math/core"
11
12
  require_relative "math/number"
12
13
  require_relative "math/symbol"
@@ -191,8 +191,12 @@ module Plurimath
191
191
  elsif flatten_mrow&.first&.is_nary_function? && flatten_mrow.length == 2
192
192
  nary_function = flatten_mrow.first
193
193
  if nary_function.is_ternary_function? && nary_function.all_values_exist?
194
- flatten_mrow[0] = nary_function.new_nary_function(flatten_mrow.delete_at(1))
195
- flatten_mrow
194
+ if nary_function.respond_to?(:new_nary_function)
195
+ flatten_mrow[0] = nary_function.new_nary_function(flatten_mrow.delete_at(1))
196
+ flatten_mrow
197
+ else
198
+ Utility.filter_values(flatten_mrow)
199
+ end
196
200
  elsif nary_function.is_binary_function? && nary_function.any_value_exist?
197
201
  flatten_mrow[0] = nary_function.new_nary_function(flatten_mrow.delete_at(1))
198
202
  flatten_mrow
@@ -0,0 +1,32 @@
1
+ require "plurimath/formatter/number_formatter"
2
+
3
+ module Plurimath
4
+ class NumberFormatter
5
+ attr_accessor :locale, :localize_number, :localizer_symbols
6
+
7
+ def initialize(locale = "en", localize_number: nil, localizer_symbols: {})
8
+ @locale = supported_locale(locale)
9
+ @localize_number = localize_number
10
+ @localizer_symbols = localizer_symbols
11
+ end
12
+
13
+ def localized_number(number_string, locale: @locale, precision: nil, format: {})
14
+ Formatter::NumberFormatter.new(
15
+ supported_locale(locale),
16
+ localize_number: localize_number,
17
+ localizer_symbols: localizer_symbols,
18
+ ).localized_number(
19
+ number_string,
20
+ locale: supported_locale(locale),
21
+ precision: precision,
22
+ format: format,
23
+ )
24
+ end
25
+
26
+ private
27
+
28
+ def supported_locale(locale)
29
+ TwitterCldr.supported_locale?(locale.to_sym) ? locale.to_sym : :en
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plurimath
4
- VERSION = "0.8.1"
4
+ VERSION = "0.8.3"
5
5
  end
data/plurimath.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
+ spec.add_dependency 'twitter_cldr'
27
28
  spec.add_dependency 'parslet'
28
29
  spec.add_dependency 'ox'
29
30
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plurimath
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-11 00:00:00.000000000 Z
11
+ date: 2024-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: twitter_cldr
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: parslet
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -70,6 +84,10 @@ files:
70
84
  - lib/plurimath/asciimath/parse.rb
71
85
  - lib/plurimath/asciimath/parser.rb
72
86
  - lib/plurimath/asciimath/transform.rb
87
+ - lib/plurimath/formatter/number_formatter.rb
88
+ - lib/plurimath/formatter/twitter_cldr_rb/fraction.rb
89
+ - lib/plurimath/formatter/twitter_cldr_rb/integer.rb
90
+ - lib/plurimath/formatter/twitter_cldr_rb/number_formatter.rb
73
91
  - lib/plurimath/html.rb
74
92
  - lib/plurimath/html/constants.rb
75
93
  - lib/plurimath/html/parse.rb
@@ -214,6 +232,7 @@ files:
214
232
  - lib/plurimath/mathml/constants.rb
215
233
  - lib/plurimath/mathml/parser.rb
216
234
  - lib/plurimath/mathml/transform.rb
235
+ - lib/plurimath/number_formatter.rb
217
236
  - lib/plurimath/omml.rb
218
237
  - lib/plurimath/omml/parser.rb
219
238
  - lib/plurimath/omml/transform.rb
@@ -259,7 +278,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
259
278
  - !ruby/object:Gem::Version
260
279
  version: '0'
261
280
  requirements: []
262
- rubygems_version: 3.3.26
281
+ rubygems_version: 3.3.27
263
282
  signing_key:
264
283
  specification_version: 4
265
284
  summary: Converts LaTeX math into MathML.