ada_numbers 1.0.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 +7 -0
- data/.idea/.gitignore +8 -0
- data/.idea/ada_numbers.iml +46 -0
- data/.idea/modules.xml +9 -0
- data/.idea/vcs.xml +6 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +21 -0
- data/Rakefile +10 -0
- data/ada_numbers.gemspec +30 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/ada_numbers/constants/en/separators.rb +11 -0
- data/lib/ada_numbers/constants/en/written_numbers.rb +113 -0
- data/lib/ada_numbers/constants/message.rb +8 -0
- data/lib/ada_numbers/constants/pt/separators.rb +9 -0
- data/lib/ada_numbers/constants/pt/written_numbers.rb +116 -0
- data/lib/ada_numbers/converters/en/number_to_words_converter.rb +185 -0
- data/lib/ada_numbers/converters/en/words_to_number_converter.rb +151 -0
- data/lib/ada_numbers/converters/number_to_words_converter_extensions.rb +32 -0
- data/lib/ada_numbers/converters/pt/number_to_words_converter.rb +179 -0
- data/lib/ada_numbers/converters/pt/words_to_number_converter.rb +143 -0
- data/lib/ada_numbers/converters/words_to_number_converter_extensions.rb +17 -0
- data/lib/ada_numbers/utilities/hash_extensions.rb +15 -0
- data/lib/ada_numbers/utilities/integer_extensions.rb +39 -0
- data/lib/ada_numbers/utilities/number_category.rb +11 -0
- data/lib/ada_numbers/utilities/settings.rb +35 -0
- data/lib/ada_numbers/utilities/string_extensions.rb +5 -0
- data/lib/ada_numbers/version.rb +3 -0
- data/lib/ada_numbers.rb +13 -0
- metadata +77 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
require_relative '../../utilities/integer_extensions'
|
2
|
+
require_relative '../../utilities/hash_extensions'
|
3
|
+
require_relative '../../utilities/string_extensions'
|
4
|
+
require_relative '../../utilities/settings'
|
5
|
+
require_relative '../../utilities/number_category'
|
6
|
+
|
7
|
+
require_relative '../../constants/en/written_numbers'
|
8
|
+
require_relative '../../constants/en/separators'
|
9
|
+
require_relative '../../constants/message'
|
10
|
+
|
11
|
+
module AdaNumbers
|
12
|
+
module WordsToNumberConverter
|
13
|
+
class En
|
14
|
+
@@use_short_scale = false
|
15
|
+
|
16
|
+
def self.convert(word)
|
17
|
+
select_scale
|
18
|
+
|
19
|
+
word = word.gsub(/\s+/, ' ').strip.
|
20
|
+
gsub(Separator::En::DASH, " #{Separator::En::NUMBERS_SEPARATOR} ").
|
21
|
+
gsub(Separator::En::COMMA, '').
|
22
|
+
title_case
|
23
|
+
|
24
|
+
words_to_convert = word.split " #{Separator::En::DECIMAL_SEPARATOR} "
|
25
|
+
words_to_convert = word.split " #{Separator::En::DECIMAL_SEPARATOR_ALTERNATIVE} " if words_to_convert.size == 1
|
26
|
+
|
27
|
+
case words_to_convert.size
|
28
|
+
when 1
|
29
|
+
return resolve_word word, @@use_short_scale
|
30
|
+
when 2
|
31
|
+
count_zeros = words_to_convert.last.split(' ').select { |w| w == WrittenNumber::En::ZERO }.size
|
32
|
+
|
33
|
+
whole_part = resolve_word words_to_convert.first, @@use_short_scale
|
34
|
+
decimal_part = resolve_word words_to_convert.last.gsub(WrittenNumber::En::ZERO, ""), @@use_short_scale
|
35
|
+
|
36
|
+
return Message::INVALID_NUMBER if whole_part == Message::INVALID_NUMBER || decimal_part == Message::INVALID_NUMBER
|
37
|
+
|
38
|
+
decimal_part = "#{'0' * count_zeros}#{decimal_part}" if count_zeros > 0
|
39
|
+
number = "#{whole_part}.#{decimal_part}"
|
40
|
+
|
41
|
+
return number.to_f
|
42
|
+
end
|
43
|
+
|
44
|
+
return Message::INVALID_NUMBER
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def self.resolve_word(word, use_short_scale = false)
|
50
|
+
word = word.gsub /\s+/, ' '
|
51
|
+
|
52
|
+
# Try to get a direct match from the map
|
53
|
+
number = map_number_according_to_scale word, use_short_scale
|
54
|
+
return number unless number.nil?
|
55
|
+
|
56
|
+
# It was not found a direct match, so, let's find that bastard
|
57
|
+
string_tokens = word.split ' '
|
58
|
+
numeric_tokens = []
|
59
|
+
|
60
|
+
# The algorithm consists on iterating every token so that to find its direct match
|
61
|
+
# in the map and then stack it up. If the next number to be stacked requires a multiplier,
|
62
|
+
# we find it and stack it up after popping the later numbers. When all the matches are found
|
63
|
+
# The number is their sum
|
64
|
+
(0...string_tokens.size).each do |cursor|
|
65
|
+
token = string_tokens[cursor]
|
66
|
+
|
67
|
+
case token
|
68
|
+
when token == Separator::En::NUMBERS_SEPARATOR && (cursor == 0 || cursor == string_tokens.size - 1),
|
69
|
+
token == Separator::En::NUMBERS_SEPARATOR && WrittenNumber::En.numbers_that_ignore_separator.include?(string_tokens[cursor + 1]),
|
70
|
+
token == Separator::En::NUMBERS_SEPARATOR && cursor > 0 && string_tokens[cursor - 1] == Separator::En::NUMBERS_SEPARATOR
|
71
|
+
return Message::INVALID_NUMBER
|
72
|
+
when Separator::En::NUMBERS_SEPARATOR
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
number_has_incorrect_or_no_separator =
|
77
|
+
# cursor is valid for evaluation
|
78
|
+
cursor > 0 && (cursor + 1 < string_tokens.size - 1) &&
|
79
|
+
# number is not part of number that ignore separator nor its next neighbour
|
80
|
+
!WrittenNumber::En.numbers_that_ignore_separator.include?(token) &&
|
81
|
+
!WrittenNumber::En.numbers_that_ignore_separator.include?(string_tokens[cursor + 1]) &&
|
82
|
+
!WrittenNumber::En.unities_that_combine_with_separator.include?(token) &&
|
83
|
+
string_tokens[cursor - 1] != Separator::En::NUMBERS_SEPARATOR
|
84
|
+
|
85
|
+
number_is_in_incorrect_short_scale_format = use_short_scale && cursor > 0 &&
|
86
|
+
cursor < string_tokens.size - 1 && token == WrittenNumber::En::THOUSAND &&
|
87
|
+
WrittenNumber::En.not_to_combine_with_thousand.include?(string_tokens[cursor + 1])
|
88
|
+
|
89
|
+
return Message::INVALID_NUMBER if number_has_incorrect_or_no_separator || number_is_in_incorrect_short_scale_format
|
90
|
+
|
91
|
+
number = map_number_according_to_scale token, use_short_scale
|
92
|
+
return Message::INVALID_NUMBER if number.nil?
|
93
|
+
|
94
|
+
if compute_multiplier? token, numeric_tokens.size
|
95
|
+
multiplier, numeric_tokens = find_multiplier numeric_tokens
|
96
|
+
number *= multiplier
|
97
|
+
end
|
98
|
+
|
99
|
+
numeric_tokens.push number
|
100
|
+
end
|
101
|
+
|
102
|
+
numeric_tokens.sum
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.map_number_according_to_scale(word, use_short_scale)
|
106
|
+
mapped_number = WrittenNumber::En.words_to_number_map.resolve(word)
|
107
|
+
|
108
|
+
number = if use_short_scale
|
109
|
+
mapped_number.nil? ? WrittenNumber::En.words_to_number_map_short_scale.resolve(word) : mapped_number
|
110
|
+
else
|
111
|
+
mapped_number.nil? ? WrittenNumber::En.words_to_number_map_long_scale.resolve(word) : mapped_number
|
112
|
+
end
|
113
|
+
|
114
|
+
number
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.select_scale
|
118
|
+
@@use_short_scale = Settings.scale == Settings::Parameters::SCALES[:short]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.compute_multiplier?(token, number_of_numeric_tokes)
|
122
|
+
(token == WrittenNumber::En::HUNDRED ||
|
123
|
+
token == WrittenNumber::En::THOUSAND ||
|
124
|
+
token == WrittenNumber::En::MILLION ||
|
125
|
+
token == WrittenNumber::En::BILLION ||
|
126
|
+
token == WrittenNumber::En::TRILLION) &&
|
127
|
+
number_of_numeric_tokes != 0
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.find_multiplier(numeric_tokens)
|
131
|
+
multiplier = numeric_tokens.pop
|
132
|
+
|
133
|
+
while numeric_tokens.size != 0
|
134
|
+
maybe_number = numeric_tokens.pop
|
135
|
+
break if maybe_number.nil?
|
136
|
+
|
137
|
+
number = maybe_number.to_i
|
138
|
+
|
139
|
+
if number.category > NumberCategory::HUNDRED
|
140
|
+
numeric_tokens.push(number)
|
141
|
+
break
|
142
|
+
end
|
143
|
+
|
144
|
+
multiplier += number
|
145
|
+
end
|
146
|
+
|
147
|
+
return multiplier, numeric_tokens
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative './pt/number_to_words_converter'
|
2
|
+
require_relative './en/number_to_words_converter'
|
3
|
+
|
4
|
+
class Float
|
5
|
+
def to_words
|
6
|
+
case AdaNumbers::Settings.language
|
7
|
+
when AdaNumbers::Settings::Parameters::LANGUAGES[:en]
|
8
|
+
AdaNumbers::NumberToWordsConverter::En.convert self
|
9
|
+
else
|
10
|
+
AdaNumbers::NumberToWordsConverter::Pt.convert self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_w
|
15
|
+
to_words
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Integer
|
20
|
+
def to_words
|
21
|
+
case AdaNumbers::Settings.language
|
22
|
+
when AdaNumbers::Settings::Parameters::LANGUAGES[:en]
|
23
|
+
AdaNumbers::NumberToWordsConverter::En.convert self
|
24
|
+
else
|
25
|
+
AdaNumbers::NumberToWordsConverter::Pt.convert self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_w
|
30
|
+
to_words
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require_relative '../../utilities/integer_extensions'
|
2
|
+
require_relative '../../utilities/hash_extensions'
|
3
|
+
require_relative '../../utilities/settings'
|
4
|
+
require_relative '../../utilities/number_category'
|
5
|
+
|
6
|
+
require_relative '../../constants/pt/written_numbers'
|
7
|
+
require_relative '../../constants/pt/separators'
|
8
|
+
require_relative '../../constants/message'
|
9
|
+
|
10
|
+
module AdaNumbers
|
11
|
+
module NumberToWordsConverter
|
12
|
+
class Pt
|
13
|
+
@@use_short_scale = false
|
14
|
+
@@number_tokens = []
|
15
|
+
|
16
|
+
def self.convert(number)
|
17
|
+
return convert_integer(number) if number.class == Integer
|
18
|
+
return convert_float(number)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def self.convert_integer(number)
|
23
|
+
return Message::UNSUPPORTED if number.number_of_digits > Settings::Parameters::DIGITS_LIMIT
|
24
|
+
|
25
|
+
select_scale
|
26
|
+
@@number_tokens.clear
|
27
|
+
return resolve_number number
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.convert_float(number)
|
31
|
+
select_scale
|
32
|
+
|
33
|
+
str_number = number.to_s.split '.'
|
34
|
+
|
35
|
+
str_integer_part = str_number.first
|
36
|
+
str_decimal_part = str_number.size == 1? "0" : str_number.last
|
37
|
+
|
38
|
+
return Message::UNSUPPORTED if str_integer_part.size > Settings::Parameters::DIGITS_LIMIT || str_decimal_part.size > Settings::Parameters::DIGITS_LIMIT
|
39
|
+
|
40
|
+
whole_part = str_integer_part.to_i
|
41
|
+
decimal_part = str_decimal_part.to_i
|
42
|
+
|
43
|
+
@@number_tokens.clear
|
44
|
+
result = resolve_number whole_part
|
45
|
+
|
46
|
+
return result if decimal_part == 0
|
47
|
+
|
48
|
+
result += " #{Separator::Pt::DECIMAL_SEPARATOR.downcase} "
|
49
|
+
# just aggregating the zeros
|
50
|
+
result += str_decimal_part.split('').
|
51
|
+
take_while { |c| c == "0"}.
|
52
|
+
collect { |c| WrittenNumber::Pt::ZERO}.
|
53
|
+
join ' '
|
54
|
+
|
55
|
+
@@number_tokens.clear
|
56
|
+
result += result[-1] == ' '? resolve_number(decimal_part) :
|
57
|
+
" #{resolve_number(decimal_part)}"
|
58
|
+
|
59
|
+
return result
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.select_scale
|
63
|
+
@@use_short_scale = Settings.scale == Settings::Parameters::SCALES[:short]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.resolve_number(number, flag = false)
|
67
|
+
result = ''
|
68
|
+
number_category = number.category
|
69
|
+
|
70
|
+
case number_category
|
71
|
+
when NumberCategory::UNITY
|
72
|
+
result = unities number
|
73
|
+
when NumberCategory::TEN
|
74
|
+
result = tens number
|
75
|
+
when NumberCategory::HUNDRED
|
76
|
+
result = hundreds number, flag
|
77
|
+
when NumberCategory::THOUSAND
|
78
|
+
result = thousands number
|
79
|
+
when NumberCategory::MILLION
|
80
|
+
result = millions number
|
81
|
+
when NumberCategory::THOUSAND_MILLIONS
|
82
|
+
result = thousand_millions number
|
83
|
+
when NumberCategory::BILLION
|
84
|
+
result = billions number
|
85
|
+
end
|
86
|
+
|
87
|
+
if result.empty?
|
88
|
+
str_number = number.to_s
|
89
|
+
bridge = number.bridge
|
90
|
+
|
91
|
+
flag_first_digits = (number_category == NumberCategory::HUNDRED ? number != 100 : false)
|
92
|
+
|
93
|
+
first_digits = (str_number[0...bridge] + '0'*number_category).to_i
|
94
|
+
other_digits = (str_number[bridge..-1]).to_i
|
95
|
+
|
96
|
+
flag_other_digits = other_digits != 100
|
97
|
+
|
98
|
+
resolve_number first_digits, flag_first_digits
|
99
|
+
resolve_number other_digits, flag_other_digits
|
100
|
+
else
|
101
|
+
@@number_tokens << result
|
102
|
+
end
|
103
|
+
|
104
|
+
add_separators_to_number @@number_tokens
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.add_separators_to_number(number_tokens)
|
108
|
+
result = number_tokens.first
|
109
|
+
|
110
|
+
(1...number_tokens.size).each do |cursor|
|
111
|
+
current_token = number_tokens[cursor]
|
112
|
+
|
113
|
+
if WrittenNumber::Pt.numbers_that_ignore_separator.include? current_token
|
114
|
+
result += " #{current_token}"
|
115
|
+
else
|
116
|
+
result += " #{Separator::Pt::NUMBERS_SEPARATOR.downcase} #{current_token}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
return result
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.unities(number)
|
124
|
+
WrittenNumber::Pt.numbers_to_words_map_unities.resolve number
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.tens(number)
|
128
|
+
WrittenNumber::Pt.numbers_to_words_map_tens.resolve number
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.hundreds(number, is_cent = false)
|
132
|
+
results = {
|
133
|
+
200 => WrittenNumber::Pt::TWO_HUNDRED,
|
134
|
+
300 => WrittenNumber::Pt::THREE_HUNDRED,
|
135
|
+
400 => WrittenNumber::Pt::FOUR_HUNDRED,
|
136
|
+
500 => WrittenNumber::Pt::FIVE_HUNDRED,
|
137
|
+
600 => WrittenNumber::Pt::SIX_HUNDRED,
|
138
|
+
700 => WrittenNumber::Pt::SEVEN_HUNDRED,
|
139
|
+
800 => WrittenNumber::Pt::EIGHT_HUNDRED,
|
140
|
+
900 => WrittenNumber::Pt::NINE_HUNDRED,
|
141
|
+
100 => (is_cent ? WrittenNumber::Pt::ONE_HUNDRED : WrittenNumber::Pt::ONE_HUNDRED_SINGLE)
|
142
|
+
}
|
143
|
+
|
144
|
+
return results.resolve number
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.thousands(number)
|
148
|
+
evaluate_thousands_and_over number, 1e3.to_i, WrittenNumber::Pt::THOUSAND, WrittenNumber::Pt::THOUSAND
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.millions(number)
|
152
|
+
evaluate_thousands_and_over number, 1e6.to_i, WrittenNumber::Pt::MILLION_SINGULAR, WrittenNumber::Pt::MILLION_PLURAL
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.thousand_millions(number)
|
156
|
+
singular = (@@use_short_scale ? WrittenNumber::Pt::BILLION_SINGULAR : WrittenNumber::Pt::THOUSAND_MILLION)
|
157
|
+
plural = (@@use_short_scale ? WrittenNumber::Pt::BILLION_PLURAL : WrittenNumber::Pt::THOUSAND_MILLION)
|
158
|
+
|
159
|
+
evaluate_thousands_and_over number, 1e9.to_i, singular, plural
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.billions(number)
|
163
|
+
singular = (@@use_short_scale ? WrittenNumber::Pt::TRILLION_SINGULAR : WrittenNumber::Pt::BILLION_SINGULAR)
|
164
|
+
plural = (@@use_short_scale ? WrittenNumber::Pt::TRILLION_PLURAL : WrittenNumber::Pt::BILLION_PLURAL)
|
165
|
+
|
166
|
+
evaluate_thousands_and_over number, 1e12.to_i, singular, plural
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.evaluate_thousands_and_over(number, category_identifier, singular, plural)
|
170
|
+
return singular if number == category_identifier
|
171
|
+
return '' if number%category_identifier != 0
|
172
|
+
|
173
|
+
partial_number = number.to_s[0...number.bridge].to_i
|
174
|
+
resolve_number partial_number
|
175
|
+
return plural
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require_relative '../../utilities/integer_extensions'
|
2
|
+
require_relative '../../utilities/hash_extensions'
|
3
|
+
require_relative '../../utilities/string_extensions'
|
4
|
+
require_relative '../../utilities/settings'
|
5
|
+
require_relative '../../utilities/number_category'
|
6
|
+
|
7
|
+
require_relative '../../constants/pt/written_numbers'
|
8
|
+
require_relative '../../constants/pt/separators'
|
9
|
+
require_relative '../../constants/message'
|
10
|
+
|
11
|
+
module AdaNumbers
|
12
|
+
module WordsToNumberConverter
|
13
|
+
class Pt
|
14
|
+
@@use_short_scale = false
|
15
|
+
|
16
|
+
def self.convert(word)
|
17
|
+
select_scale
|
18
|
+
|
19
|
+
word = word.gsub(/\s+/, ' ').strip.title_case
|
20
|
+
|
21
|
+
words_to_convert = word.split " #{Separator::Pt::DECIMAL_SEPARATOR} "
|
22
|
+
words_to_convert = word.split " #{Separator::Pt::DECIMAL_SEPARATOR_ALTERNATIVE} " if words_to_convert.size == 1
|
23
|
+
|
24
|
+
case words_to_convert.size
|
25
|
+
when 1
|
26
|
+
return resolve_word word, @@use_short_scale
|
27
|
+
when 2
|
28
|
+
count_zeros = words_to_convert.last.split(' ').select {|w| w == WrittenNumber::Pt::ZERO}.size
|
29
|
+
|
30
|
+
whole_part = resolve_word words_to_convert.first, @@use_short_scale
|
31
|
+
decimal_part = resolve_word words_to_convert.last.gsub(WrittenNumber::Pt::ZERO, ""), @@use_short_scale
|
32
|
+
|
33
|
+
return Message::INVALID_NUMBER if whole_part == Message::INVALID_NUMBER || decimal_part == Message::INVALID_NUMBER
|
34
|
+
|
35
|
+
decimal_part = "#{'0'*count_zeros}#{decimal_part}" if count_zeros > 0
|
36
|
+
number = "#{whole_part}.#{decimal_part}"
|
37
|
+
|
38
|
+
return number.to_f
|
39
|
+
end
|
40
|
+
|
41
|
+
return Message::INVALID_NUMBER
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def self.resolve_word(word, use_short_scale = false)
|
46
|
+
word = word.gsub /\s+/, ' '
|
47
|
+
|
48
|
+
# Try to get a direct match from the map
|
49
|
+
number = map_number_according_to_scale word, use_short_scale
|
50
|
+
return number unless number.nil?
|
51
|
+
|
52
|
+
# It was not found a direct match, so, let's find that bastard
|
53
|
+
string_tokens = word.split ' '
|
54
|
+
numeric_tokens = []
|
55
|
+
|
56
|
+
# The algorithm consists on iterating every token so that to find its direct match
|
57
|
+
# in the map and then stack it up. If the next number to be stacked requires a multiplier,
|
58
|
+
# we find it and stack it up after popping the later numbers. When all the matches are found
|
59
|
+
# The number is their sum
|
60
|
+
(0...string_tokens.size).each do |cursor|
|
61
|
+
token = string_tokens[cursor]
|
62
|
+
|
63
|
+
case token
|
64
|
+
when token == Separator::Pt::NUMBERS_SEPARATOR && (cursor == 0 || cursor == string_tokens.size - 1),
|
65
|
+
token == Separator::Pt::NUMBERS_SEPARATOR && WrittenNumber::Pt.numbers_that_ignore_separator.include?(string_tokens[cursor + 1]),
|
66
|
+
token == Separator::Pt::NUMBERS_SEPARATOR && cursor > 0 && string_tokens[cursor - 1] == Separator::Pt::NUMBERS_SEPARATOR
|
67
|
+
return Message::INVALID_NUMBER
|
68
|
+
when Separator::Pt::NUMBERS_SEPARATOR
|
69
|
+
next
|
70
|
+
end
|
71
|
+
|
72
|
+
token = "#{WrittenNumber::Pt::ONE} #{token}" if join_one? token
|
73
|
+
|
74
|
+
number_has_incorrect_or_no_separator = cursor > 0 &&
|
75
|
+
!WrittenNumber::Pt.numbers_that_ignore_separator.include?(token) &&
|
76
|
+
string_tokens[cursor - 1] != Separator::Pt::NUMBERS_SEPARATOR
|
77
|
+
|
78
|
+
number_is_in_incorrect_short_scale_format = use_short_scale && cursor > 0 &&
|
79
|
+
cursor < string_tokens.size - 1 && token == WrittenNumber::Pt::THOUSAND &&
|
80
|
+
WrittenNumber::Pt.not_to_combine_with_thousand.include?(string_tokens[cursor + 1])
|
81
|
+
|
82
|
+
return Message::INVALID_NUMBER if number_has_incorrect_or_no_separator || number_is_in_incorrect_short_scale_format
|
83
|
+
|
84
|
+
number = map_number_according_to_scale token, use_short_scale
|
85
|
+
return Message::INVALID_NUMBER if number.nil?
|
86
|
+
|
87
|
+
if compute_multiplier? token, numeric_tokens.size
|
88
|
+
multiplier, numeric_tokens = find_multiplier numeric_tokens
|
89
|
+
number *= multiplier
|
90
|
+
end
|
91
|
+
|
92
|
+
numeric_tokens.push number
|
93
|
+
end
|
94
|
+
|
95
|
+
numeric_tokens.sum
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.map_number_according_to_scale(word, use_short_scale)
|
99
|
+
mapped_number = WrittenNumber::Pt.words_to_number_map.resolve(word)
|
100
|
+
|
101
|
+
number = if use_short_scale
|
102
|
+
mapped_number.nil? ? WrittenNumber::Pt.words_to_number_map_short_scale.resolve(word) : mapped_number
|
103
|
+
else
|
104
|
+
mapped_number.nil? ? WrittenNumber::Pt.words_to_number_map_long_scale.resolve(word) : mapped_number
|
105
|
+
end
|
106
|
+
|
107
|
+
number
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.select_scale
|
111
|
+
@@use_short_scale = Settings.scale == Settings::Parameters::SCALES[:short]
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.join_one?(token)
|
115
|
+
token != WrittenNumber::Pt::ONE && token != WrittenNumber::Pt::THOUSAND &&
|
116
|
+
(WrittenNumber::Pt::MILLION_SINGULAR.include?(token) ||
|
117
|
+
WrittenNumber::Pt::BILLION_SINGULAR.include?(token) ||
|
118
|
+
WrittenNumber::Pt::TRILLION_SINGULAR.include?(token))
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.compute_multiplier?(token, number_of_numeric_tokes)
|
122
|
+
(token == WrittenNumber::Pt::THOUSAND ||
|
123
|
+
token == WrittenNumber::Pt::MILLION_SINGULAR ||
|
124
|
+
token == WrittenNumber::Pt::MILLION_PLURAL ||
|
125
|
+
token == WrittenNumber::Pt::BILLION_SINGULAR ||
|
126
|
+
token == WrittenNumber::Pt::BILLION_PLURAL ||
|
127
|
+
token == WrittenNumber::Pt::TRILLION_SINGULAR ||
|
128
|
+
token == WrittenNumber::Pt::TRILLION_PLURAL) &&
|
129
|
+
number_of_numeric_tokes != 0
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.find_multiplier(numeric_tokens)
|
133
|
+
multiplier = numeric_tokens.pop
|
134
|
+
|
135
|
+
while numeric_tokens.size != 0
|
136
|
+
multiplier += numeric_tokens.pop
|
137
|
+
end
|
138
|
+
|
139
|
+
return multiplier, numeric_tokens
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative './pt/words_to_number_converter'
|
2
|
+
require_relative './en/words_to_number_converter'
|
3
|
+
|
4
|
+
class String
|
5
|
+
def to_number
|
6
|
+
case AdaNumbers::Settings.language
|
7
|
+
when AdaNumbers::Settings::Parameters::LANGUAGES[:en]
|
8
|
+
AdaNumbers::WordsToNumberConverter::En.convert self
|
9
|
+
else
|
10
|
+
AdaNumbers::WordsToNumberConverter::Pt.convert self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_n
|
15
|
+
to_number
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Hash
|
2
|
+
def resolve(value)
|
3
|
+
return resolve_for_int_str(value) if value.class == Integer
|
4
|
+
return resolve_for_str_int(value)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
def resolve_for_int_str(number)
|
9
|
+
(self[number].nil?)? '' : self[number]
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve_for_str_int(words)
|
13
|
+
self[words]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Integer
|
2
|
+
extend AdaNumbers
|
3
|
+
def number_of_digits
|
4
|
+
self.to_s.size
|
5
|
+
end
|
6
|
+
|
7
|
+
def category
|
8
|
+
case self.number_of_digits
|
9
|
+
when 2
|
10
|
+
return AdaNumbers::NumberCategory::TEN
|
11
|
+
when 3
|
12
|
+
return AdaNumbers::NumberCategory::HUNDRED
|
13
|
+
when 4..6
|
14
|
+
return AdaNumbers::NumberCategory::THOUSAND
|
15
|
+
when 7..9
|
16
|
+
return AdaNumbers::NumberCategory::MILLION
|
17
|
+
when 10..12
|
18
|
+
return AdaNumbers::NumberCategory::THOUSAND_MILLIONS
|
19
|
+
when 13..15
|
20
|
+
return AdaNumbers::NumberCategory::BILLION
|
21
|
+
end
|
22
|
+
|
23
|
+
AdaNumbers::NumberCategory::UNITY
|
24
|
+
end
|
25
|
+
|
26
|
+
def bridge
|
27
|
+
n_digits = self.number_of_digits
|
28
|
+
|
29
|
+
case n_digits
|
30
|
+
when 5, 8, 11, 14
|
31
|
+
return 2
|
32
|
+
when 6, 9, 12, 15
|
33
|
+
return 3
|
34
|
+
end
|
35
|
+
|
36
|
+
return 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AdaNumbers
|
4
|
+
class Settings
|
5
|
+
class Parameters
|
6
|
+
LANGUAGES = {
|
7
|
+
en: :english,
|
8
|
+
pt: :portuguese
|
9
|
+
}
|
10
|
+
|
11
|
+
SCALES = {
|
12
|
+
short: :short,
|
13
|
+
long: :long
|
14
|
+
}
|
15
|
+
|
16
|
+
DIGITS_LIMIT = 15
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :hello
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@language = Parameters::LANGUAGES[:pt]
|
24
|
+
@scale = Parameters::SCALES[:long]
|
25
|
+
|
26
|
+
class << self
|
27
|
+
attr_accessor :language, :scale
|
28
|
+
|
29
|
+
def reset
|
30
|
+
@language = Parameters::LANGUAGES[:pt]
|
31
|
+
@scale = Parameters::SCALES[:long]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/ada_numbers.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'ada_numbers/version'
|
2
|
+
|
3
|
+
require 'ada_numbers/converters/number_to_words_converter_extensions'
|
4
|
+
require 'ada_numbers/converters/pt/number_to_words_converter'
|
5
|
+
require 'ada_numbers/converters/en/number_to_words_converter'
|
6
|
+
|
7
|
+
require 'ada_numbers/converters/words_to_number_converter_extensions'
|
8
|
+
require 'ada_numbers/converters/pt/words_to_number_converter'
|
9
|
+
|
10
|
+
module AdaNumbers
|
11
|
+
class Error < StandardError; end
|
12
|
+
# Your code goes here...
|
13
|
+
end
|