plurimath 0.8.1 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
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.