plurimath 0.10.7 → 0.11.0

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +3 -2
  3. data/README.adoc +343 -44
  4. data/lib/plurimath/asciimath/parse.rb +6 -2
  5. data/lib/plurimath/configuration.rb +17 -0
  6. data/lib/plurimath/deprecation.rb +81 -0
  7. data/lib/plurimath/errors/configuration_error.rb +27 -0
  8. data/lib/plurimath/errors/deprecation_error.rb +33 -0
  9. data/lib/plurimath/errors/error.rb +6 -0
  10. data/lib/plurimath/errors/formatter/unsupported_base.rb +1 -1
  11. data/lib/plurimath/errors/formatter/unsupported_locale.rb +18 -0
  12. data/lib/plurimath/errors/omml/unsupported_node_error.rb +1 -1
  13. data/lib/plurimath/errors/parse_error.rb +1 -1
  14. data/lib/plurimath/errors/parse_option_error.rb +34 -0
  15. data/lib/plurimath/formatter/numbers/base.rb +18 -8
  16. data/lib/plurimath/formatter/numbers/base_notation.rb +67 -0
  17. data/lib/plurimath/formatter/numbers/digit_sequence.rb +96 -0
  18. data/lib/plurimath/formatter/numbers/format_options.rb +141 -0
  19. data/lib/plurimath/formatter/numbers/fraction.rb +50 -93
  20. data/lib/plurimath/formatter/numbers/integer.rb +30 -6
  21. data/lib/plurimath/formatter/numbers/notation_renderer.rb +128 -0
  22. data/lib/plurimath/formatter/numbers/number_renderer.rb +66 -0
  23. data/lib/plurimath/formatter/numbers/parts.rb +69 -0
  24. data/lib/plurimath/formatter/numbers/parts_renderer.rb +30 -0
  25. data/lib/plurimath/formatter/numbers/precision_resolver.rb +54 -0
  26. data/lib/plurimath/formatter/numbers/sign_renderer.rb +28 -0
  27. data/lib/plurimath/formatter/numbers/significant.rb +77 -103
  28. data/lib/plurimath/formatter/numbers/source.rb +120 -0
  29. data/lib/plurimath/formatter/numbers/symbol_resolver.rb +55 -0
  30. data/lib/plurimath/formatter/numbers.rb +11 -0
  31. data/lib/plurimath/formatter/standard.rb +32 -42
  32. data/lib/plurimath/formatter/supported_locales.rb +27 -0
  33. data/lib/plurimath/formatter.rb +1 -2
  34. data/lib/plurimath/html/constants.rb +2 -0
  35. data/lib/plurimath/html/parse.rb +77 -14
  36. data/lib/plurimath/html/parser.rb +15 -3
  37. data/lib/plurimath/html/transform.rb +193 -91
  38. data/lib/plurimath/html/transform_utility.rb +61 -0
  39. data/lib/plurimath/html.rb +1 -0
  40. data/lib/plurimath/latex/parse.rb +7 -1
  41. data/lib/plurimath/latex/transform.rb +5 -5
  42. data/lib/plurimath/math/function/lim.rb +6 -0
  43. data/lib/plurimath/math/number.rb +8 -2
  44. data/lib/plurimath/math/symbols/cdot.rb +1 -1
  45. data/lib/plurimath/math/symbols/exclam.rb +1 -1
  46. data/lib/plurimath/math/symbols/minus.rb +1 -1
  47. data/lib/plurimath/math/symbols/percent.rb +1 -1
  48. data/lib/plurimath/math/symbols/pi.rb +1 -1
  49. data/lib/plurimath/math/symbols/slash.rb +1 -1
  50. data/lib/plurimath/math.rb +56 -8
  51. data/lib/plurimath/number_formatter.rb +57 -27
  52. data/lib/plurimath/unicode_math/parse.rb +7 -1
  53. data/lib/plurimath/unicode_math/transform.rb +2 -2
  54. data/lib/plurimath/version.rb +1 -1
  55. data/lib/plurimath.rb +23 -1
  56. metadata +21 -4
  57. data/lib/plurimath/formatter/number_formatter.rb +0 -115
  58. data/lib/plurimath/formatter/numeric_formatter.rb +0 -187
@@ -1,187 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Plurimath
4
- module Formatter
5
- class NumericFormatter
6
- attr_accessor :locale, :localize_number, :localizer_symbols,
7
- :twitter_cldr_reader
8
-
9
- LOCALIZE_NUMBER_REGEX = %r{(?<group>[^#])?(?<groupdigits>#+0)(?<decimal>.)(?<fractdigits>#+)(?<fractgroup>[^#])?}
10
- SUPPORTED_NOTATIONS = %i[e scientific engineering].freeze
11
-
12
- def initialize(locale, localize_number:, localizer_symbols:)
13
- @locale = locale
14
- @localize_number = localize_number
15
- @localizer_symbols = localizer_symbols
16
- @twitter_cldr_reader = twitter_cldr_reader_lookup
17
- end
18
-
19
- def localized_number(number_string, locale:, precision:, format:)
20
- options_instance_variables(number_string, format, precision)
21
- @twitter_cldr_reader.merge!(format)
22
- if SUPPORTED_NOTATIONS.include?(@notation&.to_sym)
23
- return send("#{@notation}_format",
24
- number_string)
25
- end
26
-
27
- localize_number(number_string)
28
- end
29
-
30
- private
31
-
32
- def twitter_cldr_reader_lookup
33
- symbols = Formatter::SupportedLocales::LOCALES[locale.to_sym]
34
- symbols
35
- .merge!(@localizer_symbols)
36
- .merge!(parse_localize_number)
37
- end
38
-
39
- def parse_localize_number
40
- @localize_number or return {}
41
- m = LOCALIZE_NUMBER_REGEX.match(@localize_number) or return {}
42
- {
43
- decimal: m[:decimal],
44
- group_digits: m[:groupdigits].size,
45
- fraction_group_digits: m[:fractdigits].size,
46
- group: m[:group] == " " ? "\u00A0" : (m[:group] || ""),
47
- fraction_group: m[:fractgroup] == " " ? "\u00A0" : (m[:fractgroup] || ""),
48
- }.compact
49
- end
50
-
51
- def localize_number(num)
52
- Formatter::NumberFormatter.new(
53
- BigDecimal(num),
54
- @twitter_cldr_reader,
55
- ).format(
56
- precision: @precision,
57
- )
58
- end
59
-
60
- def e_format(num_str)
61
- notations_formatting(num_str).join(@e.to_s)
62
- end
63
-
64
- def scientific_format(num_str)
65
- notations_formatting(num_str).join(" #{@times} 10^")
66
- end
67
-
68
- def engineering_format(num_str)
69
- @precision = num_str.length - 1 unless @precision.positive?
70
-
71
- chars = notation_chars(num_str)
72
- update_string_index(chars, chars.last.to_i % 3)
73
- chars[0] = localize_number(chars[0])
74
- chars.join(" #{@times} 10^")
75
- end
76
-
77
- def update_exponent_value(number_str)
78
- exponent_number = BigDecimal(number_str) - 1
79
- return exponent_number.to_i if exponent_number.negative? || @exponent_sign.to_s != "plus"
80
-
81
- "+#{exponent_number.to_i}"
82
- end
83
-
84
- def notation_chars(num_str)
85
- bd = BigDecimal(num_str)
86
- return [num_str, 0] if bd.zero?
87
-
88
- notation_array = bd.to_s("e").split("e")
89
- notation_array[1] = update_exponent_value(notation_array[1])
90
- number_str = notation_array[0]
91
- number_str = number_str.gsub(/0\.(\d)/, '\1.')
92
- number_str = number_str.sub(".", "") if number_str.start_with?(".")
93
- notation_array[0] =
94
- number_str.end_with?(".") ? number_str[0..-2] : number_str
95
- notation_array
96
- end
97
-
98
- def notations_formatting(num_str)
99
- chars = notation_chars(num_str)
100
- chars[0] = localize_number(chars[0])
101
- chars << "0" if chars.length == 1
102
- chars
103
- end
104
-
105
- def options_instance_variables(string, format, precision)
106
- @e = format[:e]&.to_sym || :e
107
- @times = format[:times]&.to_sym || "\u{d7}"
108
- @notation = format[:notation]&.to_sym || nil
109
- @precision = update_precision(string, precision, format)
110
- @exponent_sign = format[:exponent_sign]&.to_sym || nil
111
- end
112
-
113
- def update_precision(num, precision, format)
114
- return precision if precision
115
-
116
- significant_precision = significant_base_precision(num, format)
117
- return significant_precision if significant_precision
118
-
119
- if SUPPORTED_NOTATIONS.include?(@notation&.to_sym)
120
- return num.sub(".",
121
- "").size - 1
122
- end
123
-
124
- num.include?(".") ? num.sub(/^.*\./, "").size : 0
125
- end
126
-
127
- # When precision is omitted, infer the target-base fractional digits
128
- # needed to satisfy :significant. Cap the count to the source significant
129
- # digits so base conversion does not invent precision for values like 0.1.
130
- # Add one extra digit when the source has more significant digits so
131
- # Significant can still perform the final rounding pass.
132
- def significant_base_precision(num, format)
133
- base = format[:base] || Formatter::NumberFormatter::DEFAULT_BASE
134
- return unless target_base?(base)
135
-
136
- significant = format[:significant].to_i
137
- return if significant.zero?
138
-
139
- return 0 unless source_fractional?(num)
140
-
141
- source_significant = source_significant_digits(num)
142
- effective_significant = [significant, source_significant].min
143
- target_precision = [
144
- effective_significant - target_base_integer_length(num, base),
145
- 0,
146
- ].max
147
-
148
- target_precision += 1 if source_significant > effective_significant
149
- target_precision
150
- end
151
-
152
- def target_base?(base)
153
- Formatter::NumberFormatter::DEFAULT_BASE_PREFIXES.key?(base) &&
154
- base != Formatter::NumberFormatter::DEFAULT_BASE
155
- end
156
-
157
- def source_fractional?(num)
158
- mantissa, exponent = num.to_s.downcase.split("e", 2)
159
- fraction_length = mantissa.split(".", 2)[1].to_s.length
160
-
161
- fraction_length > exponent.to_i
162
- end
163
-
164
- def source_significant_digits(num)
165
- mantissa = num.to_s.downcase.split("e", 2).first
166
- mantissa.sub(/\A[-+]/, "").delete(".").sub(/\A0+/, "").length
167
- end
168
-
169
- def target_base_integer_length(num, base)
170
- integer = BigDecimal(num).abs.to_i
171
- return 0 if integer.zero?
172
-
173
- integer.to_s(base).length
174
- end
175
-
176
- def update_string_index(chars, index)
177
- return if index.zero?
178
-
179
- chars.first.delete!(".")
180
- chars.first.insert(index + 1, ".") unless chars.first[index + 2].nil?
181
- exponent = chars[-1]
182
- chars[-1] =
183
- "#{'+' if exponent.to_s.start_with?('+')}#{exponent.to_i - index}"
184
- end
185
- end
186
- end
187
- end