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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +3 -2
- data/README.adoc +343 -44
- data/lib/plurimath/asciimath/parse.rb +6 -2
- data/lib/plurimath/configuration.rb +17 -0
- data/lib/plurimath/deprecation.rb +81 -0
- data/lib/plurimath/errors/configuration_error.rb +27 -0
- data/lib/plurimath/errors/deprecation_error.rb +33 -0
- data/lib/plurimath/errors/error.rb +6 -0
- data/lib/plurimath/errors/formatter/unsupported_base.rb +1 -1
- data/lib/plurimath/errors/formatter/unsupported_locale.rb +18 -0
- data/lib/plurimath/errors/omml/unsupported_node_error.rb +1 -1
- data/lib/plurimath/errors/parse_error.rb +1 -1
- data/lib/plurimath/errors/parse_option_error.rb +34 -0
- data/lib/plurimath/formatter/numbers/base.rb +18 -8
- data/lib/plurimath/formatter/numbers/base_notation.rb +67 -0
- data/lib/plurimath/formatter/numbers/digit_sequence.rb +96 -0
- data/lib/plurimath/formatter/numbers/format_options.rb +141 -0
- data/lib/plurimath/formatter/numbers/fraction.rb +50 -93
- data/lib/plurimath/formatter/numbers/integer.rb +30 -6
- data/lib/plurimath/formatter/numbers/notation_renderer.rb +128 -0
- data/lib/plurimath/formatter/numbers/number_renderer.rb +66 -0
- data/lib/plurimath/formatter/numbers/parts.rb +69 -0
- data/lib/plurimath/formatter/numbers/parts_renderer.rb +30 -0
- data/lib/plurimath/formatter/numbers/precision_resolver.rb +54 -0
- data/lib/plurimath/formatter/numbers/sign_renderer.rb +28 -0
- data/lib/plurimath/formatter/numbers/significant.rb +77 -103
- data/lib/plurimath/formatter/numbers/source.rb +120 -0
- data/lib/plurimath/formatter/numbers/symbol_resolver.rb +55 -0
- data/lib/plurimath/formatter/numbers.rb +11 -0
- data/lib/plurimath/formatter/standard.rb +32 -42
- data/lib/plurimath/formatter/supported_locales.rb +27 -0
- data/lib/plurimath/formatter.rb +1 -2
- data/lib/plurimath/html/constants.rb +2 -0
- data/lib/plurimath/html/parse.rb +77 -14
- data/lib/plurimath/html/parser.rb +15 -3
- data/lib/plurimath/html/transform.rb +193 -91
- data/lib/plurimath/html/transform_utility.rb +61 -0
- data/lib/plurimath/html.rb +1 -0
- data/lib/plurimath/latex/parse.rb +7 -1
- data/lib/plurimath/latex/transform.rb +5 -5
- data/lib/plurimath/math/function/lim.rb +6 -0
- data/lib/plurimath/math/number.rb +8 -2
- data/lib/plurimath/math/symbols/cdot.rb +1 -1
- data/lib/plurimath/math/symbols/exclam.rb +1 -1
- data/lib/plurimath/math/symbols/minus.rb +1 -1
- data/lib/plurimath/math/symbols/percent.rb +1 -1
- data/lib/plurimath/math/symbols/pi.rb +1 -1
- data/lib/plurimath/math/symbols/slash.rb +1 -1
- data/lib/plurimath/math.rb +56 -8
- data/lib/plurimath/number_formatter.rb +57 -27
- data/lib/plurimath/unicode_math/parse.rb +7 -1
- data/lib/plurimath/unicode_math/transform.rb +2 -2
- data/lib/plurimath/version.rb +1 -1
- data/lib/plurimath.rb +23 -1
- metadata +21 -4
- data/lib/plurimath/formatter/number_formatter.rb +0 -115
- data/lib/plurimath/formatter/numeric_formatter.rb +0 -187
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
module Deprecation
|
|
5
|
+
BEHAVIORS = %i[collect raise silence].freeze
|
|
6
|
+
DEFAULT_BEHAVIOR = :collect
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def warn(feature:, message: nil, replacement: nil, since: nil, remove_in: nil)
|
|
10
|
+
feature = validate_feature(feature)
|
|
11
|
+
return if behavior == :collect && emitted_features[feature]
|
|
12
|
+
|
|
13
|
+
notice = build_notice(
|
|
14
|
+
feature: feature,
|
|
15
|
+
message: message,
|
|
16
|
+
replacement: replacement,
|
|
17
|
+
since: since,
|
|
18
|
+
remove_in: remove_in,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
case behavior
|
|
22
|
+
when :silence
|
|
23
|
+
nil
|
|
24
|
+
when :raise
|
|
25
|
+
raise notice
|
|
26
|
+
when :collect
|
|
27
|
+
emitted_features[feature] = notice
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def behavior
|
|
32
|
+
@behavior ||= DEFAULT_BEHAVIOR
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def behavior=(behavior)
|
|
36
|
+
@behavior = validate_behavior(behavior)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def notices
|
|
40
|
+
emitted_features.values
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def clear!
|
|
44
|
+
@emitted_features = {}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def build_notice(feature:, message:, replacement:, since:, remove_in:)
|
|
50
|
+
DeprecationError.new(
|
|
51
|
+
feature: feature,
|
|
52
|
+
message: message,
|
|
53
|
+
replacement: replacement,
|
|
54
|
+
since: since,
|
|
55
|
+
remove_in: remove_in,
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def emitted_features
|
|
60
|
+
@emitted_features ||= {}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def validate_behavior(behavior)
|
|
64
|
+
return behavior if BEHAVIORS.include?(behavior)
|
|
65
|
+
|
|
66
|
+
raise ConfigurationError.new(
|
|
67
|
+
:unsupported_deprecation_behavior,
|
|
68
|
+
value: behavior,
|
|
69
|
+
supported: BEHAVIORS,
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def validate_feature(feature)
|
|
74
|
+
feature = feature.to_s
|
|
75
|
+
return feature unless feature.empty?
|
|
76
|
+
|
|
77
|
+
raise ConfigurationError.new(:missing_deprecation_feature)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
class ConfigurationError < Error
|
|
5
|
+
def initialize(type, value: nil, supported: nil)
|
|
6
|
+
@type = type
|
|
7
|
+
@value = value
|
|
8
|
+
@supported = supported
|
|
9
|
+
super(message)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def message
|
|
13
|
+
case @type
|
|
14
|
+
when :unsupported_deprecation_behavior
|
|
15
|
+
"unsupported deprecation behavior: #{@value.inspect}; " \
|
|
16
|
+
"expected one of #{@supported.inspect}"
|
|
17
|
+
when :missing_deprecation_feature
|
|
18
|
+
"deprecation feature must be provided"
|
|
19
|
+
when :conflicting_formatter_options
|
|
20
|
+
"formatter options cannot be used together: choose either " \
|
|
21
|
+
":padding_digits or :padding_group_digits"
|
|
22
|
+
else
|
|
23
|
+
"invalid Plurimath configuration"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
class DeprecationError < Error
|
|
5
|
+
SEVERITY = :warning
|
|
6
|
+
|
|
7
|
+
attr_reader :feature, :replacement, :since, :remove_in
|
|
8
|
+
|
|
9
|
+
def initialize(feature:, message: nil, replacement: nil, since: nil, remove_in: nil)
|
|
10
|
+
@feature = feature
|
|
11
|
+
@replacement = replacement
|
|
12
|
+
@since = since
|
|
13
|
+
@remove_in = remove_in
|
|
14
|
+
@user_message = message
|
|
15
|
+
super(to_s)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def severity
|
|
19
|
+
SEVERITY
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_s
|
|
23
|
+
deprecation = "[plurimath][DEPRECATION] #{feature} is deprecated"
|
|
24
|
+
deprecation = "#{deprecation} since #{since}" if since
|
|
25
|
+
deprecation = "#{deprecation} and will be removed in #{remove_in}" if remove_in
|
|
26
|
+
|
|
27
|
+
parts = [deprecation]
|
|
28
|
+
parts << "Use #{replacement} instead" if replacement
|
|
29
|
+
parts << @user_message if @user_message
|
|
30
|
+
"#{parts.join('. ')}."
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
module Formatter
|
|
5
|
+
class UnsupportedLocale < Plurimath::Error
|
|
6
|
+
def initialize(locale, supported_locales)
|
|
7
|
+
@locale = locale
|
|
8
|
+
@supported = supported_locales.map(&:inspect).join(", ")
|
|
9
|
+
super(message)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def message
|
|
13
|
+
"[plurimath] Unsupported locale #{@locale.inspect}. " \
|
|
14
|
+
"Supported locales are: #{@supported}."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
module Math
|
|
5
|
+
class ParseOptionError < Plurimath::Error
|
|
6
|
+
def self.unknown_options(options, supported_options:)
|
|
7
|
+
new(
|
|
8
|
+
"unknown parse #{option_label(options)}: #{format_options(options)}; " \
|
|
9
|
+
"supported parse options are #{format_options(supported_options)}",
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.unsupported_options(type, options, supported_types:)
|
|
14
|
+
new(
|
|
15
|
+
"parse #{option_label(options)} #{format_options(options)} " \
|
|
16
|
+
"#{be_verb(options)} not supported for #{type.inspect}; " \
|
|
17
|
+
"supported input types are #{format_options(supported_types)}",
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.option_label(options)
|
|
22
|
+
options.one? ? "option" : "options"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.be_verb(options)
|
|
26
|
+
options.one? ? "is" : "are"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.format_options(options)
|
|
30
|
+
options.map(&:inspect).join(", ")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -3,22 +3,25 @@
|
|
|
3
3
|
module Plurimath
|
|
4
4
|
module Formatter
|
|
5
5
|
module Numbers
|
|
6
|
+
# Shared base for formatter helpers that need resolved options, target
|
|
7
|
+
# base state, and common digit operations.
|
|
6
8
|
class Base
|
|
9
|
+
HEX_DIGITS = "abcdef"
|
|
7
10
|
HEX_ALPHANUMERIC = %w[0 1 2 3 4 5 6 7 8 9 a b c d e f].freeze
|
|
8
11
|
DEFAULT_BASE = 10
|
|
9
12
|
DIGIT_VALUE = HEX_ALPHANUMERIC.each_with_index.to_h
|
|
10
13
|
|
|
11
|
-
attr_accessor :base, :
|
|
14
|
+
attr_accessor :base, :options
|
|
12
15
|
|
|
13
|
-
def initialize(
|
|
14
|
-
@
|
|
15
|
-
@base =
|
|
16
|
+
def initialize(options)
|
|
17
|
+
@options = options
|
|
18
|
+
@base = @options.base
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
protected
|
|
19
22
|
|
|
20
23
|
def threshold
|
|
21
|
-
|
|
24
|
+
digit_sequence.threshold
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def base_default?
|
|
@@ -26,10 +29,17 @@ module Plurimath
|
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def next_mapping_char(char)
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
digit_sequence.next_digit(char)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def capitalize_hex_digits(string)
|
|
36
|
+
return string unless base == 16 && options.hex_capital == :numbers_only
|
|
37
|
+
|
|
38
|
+
string.tr(HEX_DIGITS, HEX_DIGITS.upcase)
|
|
39
|
+
end
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
def digit_sequence
|
|
42
|
+
@digit_sequence ||= DigitSequence.new(base: base)
|
|
33
43
|
end
|
|
34
44
|
end
|
|
35
45
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
module Formatter
|
|
5
|
+
module Numbers
|
|
6
|
+
# Applies base prefix/postfix notation after numeric digits have already
|
|
7
|
+
# been rendered.
|
|
8
|
+
class BaseNotation
|
|
9
|
+
DEFAULT_PREFIXES = {
|
|
10
|
+
2 => "0b",
|
|
11
|
+
8 => "0o",
|
|
12
|
+
10 => "",
|
|
13
|
+
16 => "0x",
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :base
|
|
17
|
+
|
|
18
|
+
def initialize(options)
|
|
19
|
+
@options = options
|
|
20
|
+
@base = @options.base
|
|
21
|
+
validate_base!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def apply(string)
|
|
25
|
+
rendered = upcase_hex? ? string.tr(Base::HEX_DIGITS, Base::HEX_DIGITS.upcase) : string
|
|
26
|
+
return rendered if default?
|
|
27
|
+
|
|
28
|
+
"#{base_prefix}#{rendered}#{base_postfix}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def default?
|
|
32
|
+
base == Base::DEFAULT_BASE
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.supported?(base)
|
|
36
|
+
DEFAULT_PREFIXES.key?(base)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
attr_reader :options
|
|
42
|
+
|
|
43
|
+
def base_prefix
|
|
44
|
+
return options.base_prefix if options.base_prefix?
|
|
45
|
+
# A postfix without an explicit prefix opts out of the default prefix.
|
|
46
|
+
return "" if options.base_postfix?
|
|
47
|
+
|
|
48
|
+
DEFAULT_PREFIXES[base]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def base_postfix
|
|
52
|
+
options.base_postfix.to_s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def upcase_hex?
|
|
56
|
+
base == 16 && options.hex_capital == true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def validate_base!
|
|
60
|
+
return if self.class.supported?(base)
|
|
61
|
+
|
|
62
|
+
raise UnsupportedBase.new(base, DEFAULT_PREFIXES)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
module Formatter
|
|
5
|
+
module Numbers
|
|
6
|
+
# Shared base-digit helper for counting, rounding thresholds, next-digit
|
|
7
|
+
# lookup, and carry propagation.
|
|
8
|
+
class DigitSequence
|
|
9
|
+
attr_reader :base
|
|
10
|
+
|
|
11
|
+
ZERO = "0"
|
|
12
|
+
|
|
13
|
+
def initialize(base:)
|
|
14
|
+
@base = base
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def digit?(char)
|
|
18
|
+
Base::DIGIT_VALUE.key?(char)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def significant?(char)
|
|
22
|
+
Base::DIGIT_VALUE[char]&.positive?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def digit_count(chars, stop_at: nil)
|
|
26
|
+
count = 0
|
|
27
|
+
each_countable_digit(chars, stop_at: stop_at) { count += 1 }
|
|
28
|
+
count
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def significant_digit_count(chars)
|
|
32
|
+
start_counting = false
|
|
33
|
+
count = 0
|
|
34
|
+
|
|
35
|
+
each_countable_digit(chars) do |char|
|
|
36
|
+
start_counting = true if significant?(char)
|
|
37
|
+
count += 1 if start_counting
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
count
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def max_digit?(char)
|
|
44
|
+
Base::DIGIT_VALUE[char] == base.pred
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def next_digit(char)
|
|
48
|
+
current_index = Base::DIGIT_VALUE[char]
|
|
49
|
+
return unless current_index
|
|
50
|
+
|
|
51
|
+
Base::HEX_ALPHANUMERIC[current_index + 1]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def round_up?(char)
|
|
55
|
+
value = Base::DIGIT_VALUE[char]
|
|
56
|
+
!!(value && value >= threshold)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def threshold
|
|
60
|
+
@threshold ||= base.div(2)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def increment_reversed(digits, carry: 1, skip: [], overflow: ZERO)
|
|
64
|
+
digits = digits.dup
|
|
65
|
+
return [digits, carry] unless carry.positive?
|
|
66
|
+
|
|
67
|
+
digits.each_with_index do |digit, index|
|
|
68
|
+
next if skip.include?(digit)
|
|
69
|
+
next unless digit?(digit)
|
|
70
|
+
|
|
71
|
+
if max_digit?(digit)
|
|
72
|
+
digits[index] = overflow
|
|
73
|
+
else
|
|
74
|
+
digits[index] = next_digit(digit)
|
|
75
|
+
carry = 0
|
|
76
|
+
break
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
[digits, carry]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def each_countable_digit(chars, stop_at: nil)
|
|
86
|
+
chars.each do |char|
|
|
87
|
+
break if stop_at && char == stop_at
|
|
88
|
+
next unless digit?(char)
|
|
89
|
+
|
|
90
|
+
yield(char)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plurimath
|
|
4
|
+
module Formatter
|
|
5
|
+
module Numbers
|
|
6
|
+
# Per-render view of merged formatter symbols and resolved precision.
|
|
7
|
+
class FormatOptions
|
|
8
|
+
DEFAULT_EXPONENT_SEPARATOR = :e
|
|
9
|
+
DEFAULT_DECIMAL = "."
|
|
10
|
+
DEFAULT_FRACTION_PRECISION = 3
|
|
11
|
+
DEFAULT_GROUP = ","
|
|
12
|
+
DEFAULT_GROUP_DIGITS = 3
|
|
13
|
+
DEFAULT_PADDING = "0"
|
|
14
|
+
DEFAULT_TIMES = "\u{d7}"
|
|
15
|
+
|
|
16
|
+
attr_reader :exponent_separator, :exponent_sign, :notation, :symbols,
|
|
17
|
+
:precision, :times
|
|
18
|
+
|
|
19
|
+
def initialize(
|
|
20
|
+
source = nil,
|
|
21
|
+
symbols: {},
|
|
22
|
+
precision: nil,
|
|
23
|
+
precision_resolver: nil
|
|
24
|
+
)
|
|
25
|
+
@symbols = symbols.dup
|
|
26
|
+
@notation = symbol_option(:notation)
|
|
27
|
+
@exponent_separator = symbol_option(:e) || DEFAULT_EXPONENT_SEPARATOR
|
|
28
|
+
@times = symbol_option(:times) || DEFAULT_TIMES
|
|
29
|
+
@precision = resolve_precision(source, precision, precision_resolver)
|
|
30
|
+
@exponent_sign = symbol_option(:exponent_sign)
|
|
31
|
+
validate_padding_options!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def base
|
|
35
|
+
symbols[:base] || Base::DEFAULT_BASE
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def base_prefix
|
|
39
|
+
symbols[:base_prefix].to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def base_prefix?
|
|
43
|
+
symbols.key?(:base_prefix)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def base_postfix
|
|
47
|
+
symbols[:base_postfix]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def base_postfix?
|
|
51
|
+
symbols.key?(:base_postfix)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def decimal
|
|
55
|
+
symbols.fetch(:decimal, DEFAULT_DECIMAL)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def digit_count
|
|
59
|
+
symbols[:digit_count].to_i
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def fraction_group
|
|
63
|
+
symbols[:fraction_group].to_s
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fraction_group_digits
|
|
67
|
+
symbols[:fraction_group_digits]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def group
|
|
71
|
+
symbols[:group] || DEFAULT_GROUP
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def group_digits
|
|
75
|
+
symbols[:group_digits] || DEFAULT_GROUP_DIGITS
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def hex_capital
|
|
79
|
+
symbols[:hex_capital]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def number_sign
|
|
83
|
+
symbols[:number_sign]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def padding
|
|
87
|
+
value = symbols.fetch(:padding, DEFAULT_PADDING).to_s
|
|
88
|
+
value.empty? ? DEFAULT_PADDING : value[0]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def padding_digits
|
|
92
|
+
symbols[:padding_digits].to_i
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def padding_group_digits
|
|
96
|
+
symbols[:padding_group_digits].to_i
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def notation_supported?
|
|
100
|
+
NotationRenderer.supported?(notation)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def significant
|
|
104
|
+
symbols[:significant].to_i
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def to_h
|
|
108
|
+
symbols.dup
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def resolve_precision(source, precision, precision_resolver)
|
|
114
|
+
effective_precision = precision || symbols[:precision]
|
|
115
|
+
return effective_precision unless precision_resolver
|
|
116
|
+
|
|
117
|
+
precision_resolver.resolve(
|
|
118
|
+
source,
|
|
119
|
+
precision: effective_precision,
|
|
120
|
+
base: base,
|
|
121
|
+
significant: significant,
|
|
122
|
+
notation_supported: notation_supported?,
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def symbol_option(key)
|
|
127
|
+
symbols[key]&.to_sym
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def validate_padding_options!
|
|
131
|
+
return unless symbols.key?(:padding_digits) && symbols.key?(:padding_group_digits)
|
|
132
|
+
|
|
133
|
+
raise Plurimath::ConfigurationError.new(
|
|
134
|
+
:conflicting_formatter_options,
|
|
135
|
+
supported: %i[padding_digits padding_group_digits],
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|