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