unit_measurements_us_complete 5.17.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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +31 -0
  3. data/.github/workflows/pages.yml +31 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +3 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +645 -0
  8. data/Gemfile +7 -0
  9. data/Gemfile.lock +56 -0
  10. data/LICENSE.md +21 -0
  11. data/README.md +511 -0
  12. data/Rakefile +10 -0
  13. data/lib/unit_measurements/arithmetic.rb +225 -0
  14. data/lib/unit_measurements/base.rb +177 -0
  15. data/lib/unit_measurements/cache.rb +173 -0
  16. data/lib/unit_measurements/comparison.rb +64 -0
  17. data/lib/unit_measurements/configuration.rb +64 -0
  18. data/lib/unit_measurements/conversion.rb +92 -0
  19. data/lib/unit_measurements/errors/blank_quantity_error.rb +21 -0
  20. data/lib/unit_measurements/errors/blank_unit_error.rb +21 -0
  21. data/lib/unit_measurements/errors/missing_primitive_unit_error.rb +24 -0
  22. data/lib/unit_measurements/errors/parse_error.rb +37 -0
  23. data/lib/unit_measurements/errors/primitive_unit_already_set_error.rb +22 -0
  24. data/lib/unit_measurements/errors/unit_already_defined_error.rb +37 -0
  25. data/lib/unit_measurements/errors/unit_error.rb +36 -0
  26. data/lib/unit_measurements/extras/conversion_methods.rb +87 -0
  27. data/lib/unit_measurements/extras/numeric_methods.rb +85 -0
  28. data/lib/unit_measurements/formatter.rb +58 -0
  29. data/lib/unit_measurements/math.rb +120 -0
  30. data/lib/unit_measurements/measurement.rb +539 -0
  31. data/lib/unit_measurements/normalizer.rb +155 -0
  32. data/lib/unit_measurements/parser.rb +220 -0
  33. data/lib/unit_measurements/unit.rb +243 -0
  34. data/lib/unit_measurements/unit_group.rb +297 -0
  35. data/lib/unit_measurements/unit_group_builder.rb +224 -0
  36. data/lib/unit_measurements/unit_groups/acceleration.rb +36 -0
  37. data/lib/unit_measurements/unit_groups/all.rb +56 -0
  38. data/lib/unit_measurements/unit_groups/amount_of_substance.rb +13 -0
  39. data/lib/unit_measurements/unit_groups/angular_acceleration.rb +14 -0
  40. data/lib/unit_measurements/unit_groups/angular_velocity.rb +25 -0
  41. data/lib/unit_measurements/unit_groups/area.rb +49 -0
  42. data/lib/unit_measurements/unit_groups/catalytic_activity.rb +13 -0
  43. data/lib/unit_measurements/unit_groups/density.rb +33 -0
  44. data/lib/unit_measurements/unit_groups/dynamic_viscosity.rb +22 -0
  45. data/lib/unit_measurements/unit_groups/electric_charge.rb +20 -0
  46. data/lib/unit_measurements/unit_groups/electric_conductance.rb +15 -0
  47. data/lib/unit_measurements/unit_groups/electric_current.rb +19 -0
  48. data/lib/unit_measurements/unit_groups/electric_dipole_moment.rb +13 -0
  49. data/lib/unit_measurements/unit_groups/electric_potential.rb +17 -0
  50. data/lib/unit_measurements/unit_groups/electric_quadrupole_moment.rb +14 -0
  51. data/lib/unit_measurements/unit_groups/electrical_capacitance.rb +15 -0
  52. data/lib/unit_measurements/unit_groups/electrical_elastance.rb +13 -0
  53. data/lib/unit_measurements/unit_groups/electrical_inductance.rb +15 -0
  54. data/lib/unit_measurements/unit_groups/electrical_resistance.rb +16 -0
  55. data/lib/unit_measurements/unit_groups/energy.rb +58 -0
  56. data/lib/unit_measurements/unit_groups/force.rb +47 -0
  57. data/lib/unit_measurements/unit_groups/frequency.rb +16 -0
  58. data/lib/unit_measurements/unit_groups/illuminance.rb +18 -0
  59. data/lib/unit_measurements/unit_groups/information_entropy.rb +15 -0
  60. data/lib/unit_measurements/unit_groups/kinetic_viscosity.rb +18 -0
  61. data/lib/unit_measurements/unit_groups/length.rb +67 -0
  62. data/lib/unit_measurements/unit_groups/luminance.rb +21 -0
  63. data/lib/unit_measurements/unit_groups/luminous_flux.rb +11 -0
  64. data/lib/unit_measurements/unit_groups/luminous_intensity.rb +13 -0
  65. data/lib/unit_measurements/unit_groups/magnetic_field.rb +13 -0
  66. data/lib/unit_measurements/unit_groups/magnetic_flux.rb +15 -0
  67. data/lib/unit_measurements/unit_groups/magnetic_induction.rb +13 -0
  68. data/lib/unit_measurements/unit_groups/magnetomotive_force.rb +13 -0
  69. data/lib/unit_measurements/unit_groups/mass_flow_rate.rb +49 -0
  70. data/lib/unit_measurements/unit_groups/plane_angle.rb +30 -0
  71. data/lib/unit_measurements/unit_groups/power.rb +54 -0
  72. data/lib/unit_measurements/unit_groups/pressure.rb +60 -0
  73. data/lib/unit_measurements/unit_groups/quantity.rb +14 -0
  74. data/lib/unit_measurements/unit_groups/radiation_absorbed_dose.rb +14 -0
  75. data/lib/unit_measurements/unit_groups/radiation_equivalent_dose.rb +13 -0
  76. data/lib/unit_measurements/unit_groups/radiation_exposure.rb +15 -0
  77. data/lib/unit_measurements/unit_groups/radioactivity.rb +14 -0
  78. data/lib/unit_measurements/unit_groups/solid_angle.rb +18 -0
  79. data/lib/unit_measurements/unit_groups/sound_level.rb +13 -0
  80. data/lib/unit_measurements/unit_groups/temperature.rb +19 -0
  81. data/lib/unit_measurements/unit_groups/time.rb +29 -0
  82. data/lib/unit_measurements/unit_groups/torque.rb +40 -0
  83. data/lib/unit_measurements/unit_groups/velocity.rb +37 -0
  84. data/lib/unit_measurements/unit_groups/volume.rb +67 -0
  85. data/lib/unit_measurements/unit_groups/volumetric_flow_rate.rb +35 -0
  86. data/lib/unit_measurements/unit_groups/weight.rb +55 -0
  87. data/lib/unit_measurements/version.rb +8 -0
  88. data/lib/unit_measurements.rb +7 -0
  89. data/unit_measurements.gemspec +43 -0
  90. data/units.md +843 -0
  91. metadata +216 -0
@@ -0,0 +1,220 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_stringing_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module UnitMeasurements
6
+ # The +UnitMeasurements::Parser+ class provides methods for parsing strings to
7
+ # extract quantity and associated unit. It can handle various formats, including
8
+ # complex numbers, scientific numbers, and rational numbers for the +quantity+.
9
+ #
10
+ # @see Measurement
11
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
12
+ # @since 1.0.0
13
+ class Parser
14
+ # Matches any character sequence that does not consist of digits, whitespace,
15
+ # or forward slashes.
16
+ #
17
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
18
+ # @since 1.0.0
19
+ UNIT_REGEX = /
20
+ ( # Start of the capturing group
21
+ [^\d\s\/] # Match any character that is not a digit, whitespace, or a forward slash
22
+ .* # Match zero or more of any character (except for a newline)
23
+ ) # End of the capturing group
24
+ /x.freeze
25
+
26
+ # Matches scientific numbers (e.g., +1.23e-4+).
27
+ #
28
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
29
+ # @since 1.0.0
30
+ SCIENTIFIC_NUMBER = /
31
+ ( # Start of a capturing group (denoted by parentheses)
32
+ [+-]? # Match an optional plus or minus sign (+ or -)
33
+ \d* # Match zero or more digits
34
+ \.? # Match an optional dot (.)
35
+ \d+ # Match one or more digits
36
+ (?: # Start of a non-capturing group
37
+ [Ee] # Match either 'E' or 'e'
38
+ [+-]? # Match an optional plus or minus sign (+ or -)
39
+ )? # End of the non-capturing group; ? makes it optional
40
+ \d* # Match zero or more digits
41
+ ) # End of the capturing group
42
+ /x.freeze
43
+
44
+ # Matches rational numbers (e.g., +1/2+).
45
+ #
46
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
47
+ # @since 1.0.0
48
+ RATIONAL_NUMBER = /
49
+ ( # Start of the first capturing group
50
+ [+-]? # Match an optional plus or minus sign (+ or -)
51
+ \d+ # Match one or more digits
52
+ \s+ # Match one or more whitespace characters
53
+ )? # End of the first capturing group
54
+ ( # Start of the second capturing group (the fraction part)
55
+ (\d+) # Start of the third capturing group (one or more digits, the numerator)
56
+ \/ # Match a forward slash - the division symbol
57
+ (\d+) # Start of the fourth capturing group (one or more digits, the denominator)
58
+ ) # End of the second capturing group (the fraction part)
59
+ /x.freeze
60
+
61
+ # Matches complex numbers (e.g., +1-2i+).
62
+ #
63
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
64
+ # @since 1.0.0
65
+ COMPLEX_NUMBER = /
66
+ #{SCIENTIFIC_NUMBER} # Pattern for scientific number
67
+ #{SCIENTIFIC_NUMBER} # Pattern for scientific number
68
+ i # Match the letter 'i' (the imaginary unit)
69
+ /x.freeze
70
+
71
+ # Matches strings containing scientific numbers and unit.
72
+ #
73
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
74
+ # @since 1.0.0
75
+ SCIENTIFIC_REGEX = /
76
+ \A # Anchor at the start of the string
77
+ #{SCIENTIFIC_NUMBER} # Match a scientific number (as defined earlier)
78
+ \s* # Match zero or more whitespace characters
79
+ #{UNIT_REGEX}? # Match a unit, the '?' makes it optional
80
+ \z # Anchor at the end of the string
81
+ /x.freeze
82
+
83
+ # Matches strings containing rational numbers and unit.
84
+ #
85
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
86
+ # @since 1.0.0
87
+ RATIONAL_REGEX = /
88
+ \A # Anchor at the start of the string
89
+ #{RATIONAL_NUMBER} # Match a rational number (as defined earlier)
90
+ \s* # Match zero or more whitespace characters
91
+ #{UNIT_REGEX}? # Match a unit, the '?' makes it optional
92
+ \z
93
+ /x.freeze
94
+
95
+ # Matches strings containing complex numbers and unit.
96
+ #
97
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
98
+ # @since 1.0.0
99
+ COMPLEX_REGEX = /
100
+ \A # Anchor at the start of the string
101
+ #{COMPLEX_NUMBER} # Match a complex number (as defined earlier)
102
+ \s* # Match zero or more whitespace characters
103
+ #{UNIT_REGEX}? # Match a unit, the '?' makes it optional
104
+ \z
105
+ /x.freeze
106
+
107
+ class << self
108
+ # Parses a string to extract a +quantity+ and its associated +unit+. This
109
+ # method first extracts a +quantity+ and converts it to +Float+ before
110
+ # returning it.
111
+ #
112
+ # To get the correct parsed results, you must first normalize the +string+
113
+ # with +Normalizer+ if using the parser standalone.
114
+ #
115
+ # @example Parsing string representing a complex number:
116
+ # UnitMeasurements::Parser.parse("1+2i m")
117
+ # => [(1.0+2.0i), "m"]
118
+ #
119
+ # @example Parsing string representing a rational number:
120
+ # UnitMeasurements::Parser.parse("1/2 m")
121
+ # => [0.5, "m"]
122
+ #
123
+ # @example Parsing string representing a mixed rational number:
124
+ # UnitMeasurements::Parser.parse("2 1/2 km")
125
+ # => [2.5, "km"]
126
+ #
127
+ # @example Parsing string representing a scientific number:
128
+ # UnitMeasurements::Parser.parse("1e+2 km")
129
+ # => [100.0, "km"]
130
+ #
131
+ # @param [String] string
132
+ # The input string containing a +quantity+ and an optional +unit+.
133
+ #
134
+ # @return [Array<Numeric, String|NilClass>]
135
+ # The parsed +quantity+ and the +unit+ associated with it (or +nil+ if
136
+ # no unit is specified in the +string+).
137
+ #
138
+ # @raise [ParseError] If the string is invalid and cannot be parsed.
139
+ #
140
+ # @see Measurement
141
+ # @see Normalizer
142
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
143
+ # @since 1.0.0
144
+ def parse(string)
145
+ case string
146
+ when COMPLEX_REGEX then parse_complex(string)
147
+ when SCIENTIFIC_REGEX then parse_scientific(string)
148
+ when RATIONAL_REGEX then parse_rational(string)
149
+ else raise ParseError, string
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ # @private
156
+ # Parses a string representing a complex number with an optional unit.
157
+ #
158
+ # @param [String] string
159
+ # The input string containing a complex number and an optional unit.
160
+ #
161
+ # @return [Array<Numeric, String|NilClass>]
162
+ # The parsed complex number and the associated unit (or +nil+ if no unit
163
+ # is specified in the string).
164
+ #
165
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
166
+ # @since 1.0.0
167
+ def parse_complex(string)
168
+ real, imaginary, unit = string.match(COMPLEX_REGEX)&.captures
169
+ quantity = Complex(real.to_f, imaginary.to_f)
170
+
171
+ [quantity, unit]
172
+ end
173
+
174
+ # @private
175
+ # Parses a string representing a scientific number with an optional unit.
176
+ #
177
+ # @param [String] string
178
+ # The input string containing a scientific number and an optional unit.
179
+ #
180
+ # @return [Array<Numeric, String|NilClass>]
181
+ # The parsed scientific number and the associated unit (or +nil+ if no unit
182
+ # is specified in the string).
183
+ #
184
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
185
+ # @since 1.0.0
186
+ def parse_scientific(string)
187
+ whole, unit = string.match(SCIENTIFIC_REGEX)&.captures
188
+ quantity = whole.to_f
189
+
190
+ [quantity, unit]
191
+ end
192
+
193
+ # @private
194
+ # Parses a string representing a rational number with an optional unit.
195
+ #
196
+ # @param [String] string
197
+ # The input string containing a rational number and an optional unit.
198
+ #
199
+ # @return [Array<Numeric, String|NilClass>]
200
+ # The parsed rational number and the associated unit (or +nil+ if no unit
201
+ # is specified in the string).
202
+ #
203
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
204
+ # @since 1.0.0
205
+ def parse_rational(string)
206
+ whole, _, numerator, denominator, unit = string.match(RATIONAL_REGEX)&.captures
207
+
208
+ if numerator && denominator
209
+ numerator = numerator.to_f + (denominator.to_f * whole.to_f)
210
+ denominator = denominator.to_f
211
+ quantity = Rational(numerator, denominator).to_f
212
+ else
213
+ quantity = whole.to_f
214
+ end
215
+
216
+ [quantity, unit]
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,243 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ require "set"
6
+
7
+ module UnitMeasurements
8
+ # The +UnitMeasurements::Unit+ class represents a unit of measurement and
9
+ # provides methods to interact with its properties and conversion factors.
10
+ #
11
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
12
+ # @since 1.0.0
13
+ class Unit
14
+ # The name of the unit.
15
+ #
16
+ # @return [String] Name of the unit.
17
+ #
18
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
19
+ # @since 1.0.0
20
+ attr_reader :name
21
+
22
+ # The conversion value of the unit. It can be a numeric value or a string in
23
+ # the form of a number followed by a unit name (e.g., “10 m”).
24
+ #
25
+ # @return [String|Numeric|Array<Numeric, String>]
26
+ # Conversion value of the unit.
27
+ #
28
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
29
+ # @since 1.0.0
30
+ attr_reader :value
31
+
32
+ # A set of alternative names for the unit.
33
+ #
34
+ # @return [Set<String>] A set of alternative names.
35
+ #
36
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
37
+ # @since 1.0.0
38
+ attr_reader :aliases
39
+
40
+ # The system to which the unit belongs (e.g., “metric”, “imperial”).
41
+ #
42
+ # @return [String] Unit system in which the unit belongs.
43
+ #
44
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
45
+ # @since 4.0.0
46
+ attr_reader :system
47
+
48
+ # The unit group to which the unit belongs.
49
+ #
50
+ # @return [UnitGroup] Unit group in which the unit belongs.
51
+ #
52
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
53
+ # @since 1.0.0
54
+ attr_reader :unit_group
55
+
56
+ # Initializes a new +Unit+ instance.
57
+ #
58
+ # @param [String|Symbol] name The name of the unit.
59
+ # @param [String|Numeric] value The conversion value of the unit.
60
+ # @param [Array<String|Symbol>] aliases Alternative names for the unit.
61
+ # @param [String|Symbol|NilClass] system The system to which the unit belongs.
62
+ # @param [UnitGroup|NilClass] unit_group The unit group to which the unit belongs.
63
+ #
64
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
65
+ # @since 1.0.0
66
+ def initialize(name, value:, aliases:, system:, unit_group: nil)
67
+ @name = name.to_s.freeze
68
+ @value = value
69
+ @aliases = Set.new(aliases.map(&:to_s).sort.map(&:freeze)).freeze
70
+ @system = system.to_s.freeze
71
+ @unit_group = unit_group
72
+ end
73
+
74
+ # Returns a new +Unit+ instance with specified attributes.
75
+ #
76
+ # @param [String|Symbol] name The new name of the unit.
77
+ # @param [String|Numeric] value The new conversion value of the unit.
78
+ # @param [Set<String>] aliases New alternative names for the unit.
79
+ # @param [String|Symbol|NilClass] system The new system to which the unit belongs.
80
+ # @param [UnitGroup|NilClass] unit_group The new unit group to which the unit belongs.
81
+ #
82
+ # @return [Unit] A new unit with specified parameters.
83
+ #
84
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
85
+ # @since 1.0.0
86
+ def with(name: nil, value: nil, aliases: nil, system: nil, unit_group: nil)
87
+ self.class.new(
88
+ (name || self.name),
89
+ value: (value || self.value),
90
+ aliases: (aliases || self.aliases),
91
+ system: (system || self.system),
92
+ unit_group: (unit_group || self.unit_group)
93
+ )
94
+ end
95
+
96
+ # Returns an array containing the name of the unit and its aliases, sorted
97
+ # alphabetically.
98
+ #
99
+ # @example
100
+ # UnitMeasurements::Length.new(1, "m").unit.names
101
+ # => ["m", "meter", "meters", "metre", "metres"]
102
+ #
103
+ # @return [Array<String>] An array of unit names.
104
+ #
105
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
106
+ # @since 1.0.0
107
+ def names
108
+ (aliases + [name]).sort.freeze
109
+ end
110
+
111
+ # Returns the name of the unit as a string.
112
+ #
113
+ # @example
114
+ # UnitMeasurements::Length.new(1, "m").unit.to_s
115
+ # => "m"
116
+ #
117
+ # @return [String] The name of the unit.
118
+ #
119
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
120
+ # @since 1.0.0
121
+ def to_s
122
+ name
123
+ end
124
+
125
+ # Returns an object representation of the unit, including its aliases if present.
126
+ #
127
+ # @return [Object] An object representation of the +unit+.
128
+ #
129
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
130
+ # @since 1.0.0
131
+ def inspect
132
+ aliases = "(#{@aliases.join(", ")})" if @aliases.any?
133
+ "#<#{self.class.name}: #{name} #{aliases}>"
134
+ end
135
+
136
+ # Calculates the conversion factor for the unit. This method is recursively
137
+ # invoked to calculate the conversion factor of the unit, if unit conversion
138
+ # +value+ is specified with respect to the other unit.
139
+ #
140
+ # This method uses +parse_value+ method to extract the conversion value and
141
+ # the unit.
142
+ #
143
+ # @return [Numeric] The conversion factor as a numeric value.
144
+ #
145
+ # @see #parse_value
146
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
147
+ # @since 1.0.0
148
+ def conversion_factor
149
+ return value if value.is_a?(Numeric)
150
+
151
+ measurement_value, measurement_unit = parse_value(value)
152
+ conversion_factor = unit_group.unit_for!(measurement_unit).conversion_factor
153
+
154
+ conversion_factor * measurement_value
155
+ end
156
+
157
+ private
158
+
159
+ # Binary prefixes for SI units.
160
+ #
161
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
162
+ # @since 5.0.0
163
+ SI_BINARY_PREFIXES = [
164
+ ["Ki", %w[kibi], 2.pow(10)],
165
+ ["Mi", %w[mebi], 2.pow(20)],
166
+ ["Gi", %w[gibi], 2.pow(30)],
167
+ ["Ti", %w[tebi], 2.pow(40)],
168
+ ["Pi", %w[pebi], 2.pow(50)],
169
+ ["Ei", %w[exbi], 2.pow(60)],
170
+ ["Zi", %w[zebi], 2.pow(70)],
171
+ ["Yi", %w[yobi], 2.pow(80)],
172
+ ].map(&:freeze).freeze
173
+
174
+ # Decimal prefixes for SI units.
175
+ #
176
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
177
+ # @since 1.0.0
178
+ SI_DECIMAL_PREFIXES = [
179
+ ["q", %w[quecto], 1e-30],
180
+ ["r", %w[ronto], 1e-27],
181
+ ["y", %w[yocto], 1e-24],
182
+ ["z", %w[zepto], 1e-21],
183
+ ["a", %w[atto], 1e-18],
184
+ ["f", %w[femto], 1e-15],
185
+ ["p", %w[pico], 1e-12],
186
+ ["n", %w[nano], 1e-9],
187
+ ["μ", %w[micro], 1e-6],
188
+ ["m", %w[milli], 1e-3],
189
+ ["c", %w[centi], 1e-2],
190
+ ["d", %w[deci], 1e-1],
191
+ ["da", %w[deca deka], 1e+1],
192
+ ["h", %w[hecto], 1e+2],
193
+ ["k", %w[kilo], 1e+3],
194
+ ["M", %w[mega], 1e+6],
195
+ ["G", %w[giga], 1e+9],
196
+ ["T", %w[tera], 1e+12],
197
+ ["P", %w[peta], 1e+15],
198
+ ["E", %w[exa], 1e+18],
199
+ ["Z", %w[zetta], 1e+21],
200
+ ["Y", %w[yotta], 1e+24],
201
+ ["R", %w[ronna], 1e+27],
202
+ ["Q", %w[quetta], 1e+30]
203
+ ].map(&:freeze).freeze
204
+
205
+ # Parses tokens and returns a +conversion value+ and the +unit+.
206
+ #
207
+ # This method is used internally to parse the conversion value of the unit
208
+ # while calculating the conversion factor. It handles cases where the value
209
+ # can be provided as a string or an array containing a number and a unit.
210
+ #
211
+ # For example, if the value is provided as a string in the form of "10 m",
212
+ # it will be parsed to return 10.0 as the conversion value and "m" as the
213
+ # unit.
214
+ #
215
+ # This method returns conversion value in +rational+ number to avoid precision
216
+ # errors and frozen string of unit name.
217
+ #
218
+ # @param [String|Array] tokens
219
+ # The value to be parsed. It can be either a string or an array containing
220
+ # a number and a unit.
221
+ #
222
+ # @return [Array<Numeric, String>] The array of conversion value and the unit.
223
+ #
224
+ # @raise [BaseError]
225
+ # if +tokens+ is not an instance of +Array+ or +String+, or +tokens+ array
226
+ # contains more than two elements.
227
+ #
228
+ # @see #conversion_factor
229
+ # @author {Harshal V. Ladhe}[https://shivam091.github.io/]
230
+ # @since 1.2.0
231
+ def parse_value(tokens)
232
+ case tokens
233
+ when String
234
+ tokens = Parser.parse(value)
235
+ when Array
236
+ raise BaseError, "Cannot parse [number, unit] formatted tokens from #{tokens}." unless tokens.size == 2
237
+ else
238
+ raise BaseError, "Value of the unit must be defined as string or array, but received #{tokens}"
239
+ end
240
+ [tokens[0].to_r, tokens[1].freeze]
241
+ end
242
+ end
243
+ end