figures 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de702fb97a926179fce3f9980284d4b5bb08ac84
4
- data.tar.gz: 01ce6d9a38a368679b3678670019fe44f24e9048
3
+ metadata.gz: a46955406747f8f41903de63ea0c16298d28ede2
4
+ data.tar.gz: 12fc043abf6ea720f7d009ccd2845cb0eabd1cc8
5
5
  SHA512:
6
- metadata.gz: 46e1fe163048f8b3d053217c3995fca5fd19f43fd6c3a9283b57078ea56e4e5cf733524b35c376ff790a396e6efee500102f305cfaa7971002129f6734cc0fa8
7
- data.tar.gz: e536e4b30f22cae941cef4497949de67f85764f824e1ffcb04d915ec4a304d3cafd0d0adccd328b5f52a8f6f6ab93d31de4077984668146535c832619e1ff386
6
+ metadata.gz: 7f9f475009fc3ecc748fa0c5e71c7326225f8c078477513e45d5a2c85d9a0e440b6b40f0c350ee4ea17dc566197b0a23e97c9c99dcbd55ed9356de24f03ac038
7
+ data.tar.gz: 6ef00052728d61532d0dcd7133ba3dab7da4c904f8a8e8d6c691d0492c1c420bd6c7c1df62aac1b5bcaa699c3842c1b9453d6c4c0fe2ed6f43f912a80243ef42
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  Gemfile.lock
2
+ /pkg
@@ -1,5 +1,10 @@
1
1
  ## CHANGELOG
2
2
 
3
+ ### 0.2.0
4
+
5
+ * Support even larger numbers
6
+ * Improve German edge cases
7
+
3
8
  ### 0.1.0
4
9
 
5
10
  * Inital release
@@ -1,4 +1,5 @@
1
1
  Copyright (c) 2015 Paul Götze, paul.christoph.goetze@gmail.com
2
+ Copyright (c) 2015 Jan Lelis, mail@janlelis.de
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining
4
5
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -15,10 +15,13 @@ gem 'figures'
15
15
  ```ruby
16
16
  require 'figures'
17
17
 
18
- Figures.parse(1234, :de) # => 'eintausendzweihundertvierunddreißig'
18
+ >> Figures.parse(1234, :de)
19
+ => 'eintausendzweihundertvierunddreißig'
20
+ >> Figures.parse(11948643178718557106276991626757751015195481434, :de)
21
+ => "elf Septilliarden neunhundertachtundvierzig Septillionen sechshundertdreiundvierzig Sextilliarden einhundertachtundsiebzig Sextillionen siebenhundertachtzehn Quintilliarden fünfhundertsiebenundfünfzig Quintillionen einhundertsechs Quadrilliarden zweihundertsechsundsiebzig Quadrillionen neunhunderteinundneunzig Trilliarden sechshundertsechsundzwanzig Trillionen siebenhundertsiebenundfünfzig Billiarden siebenhunderteinundfünfzig Billionen fünfzehn Milliarden einhundertfünfundneunzig Millionen vierhunderteinundachtzigtausendvierhundertvierunddreißig"
19
22
  ```
20
23
 
21
24
 
22
25
  ## MIT License
23
26
 
24
- Copyright (C) 2015 Paul Götze. Released under the MIT license.
27
+ Copyright (C) 2015 Paul Götze, Jan Lelis. Released under the MIT license.
@@ -1,9 +1,8 @@
1
1
  require_relative "figures/version"
2
-
3
2
  require_relative "figures/german"
4
3
 
5
4
  module Figures
6
5
  def self.parse(number, language_code = :de)
7
- Figures::German.new(number.to_s).parse
6
+ Figures::German.new(number).parse
8
7
  end
9
8
  end
@@ -1,89 +1,137 @@
1
1
  module Figures
2
2
  class German
3
- WORDS = {
4
- copula: "und",
5
- digits: %w[null ein zwei drei vier fünf sechs sieben acht neun],
6
- tens: %w[eine zehn zwanzig dreißig vierzig fünfzig sechzig siebzig achzig neunzig],
7
- exponents: %w[hundert tausend million milliarden billion billiarden trillion trilliarden quadrillionen quadrilliarden quintillion sextillion sextilliarden]
3
+ UNITS = %w{ eins zwei drei vier fünf sechs sieben acht neun }.freeze
4
+
5
+ PREFIXES = {
6
+ units: %w{ tausend mi bi tri quadri quinti sexti septi okti noni },
7
+ union_units: %w{ un duo tre quattuor quinqua se septe okto nove },
8
+ union_tens: %w{ dezi viginti triginta quadraginta quinquaginta sexaginta septuaginta oktoginta nonaginta },
9
+ union_hundreds: %w{ zenti duzenti trezenti quadringenti quingenti seszenti septingenti oktingenti nongenti }
10
+ }.freeze
11
+
12
+ EXCEPTIONS = {
13
+ /^eins(hundert|tausend)/ => 'ein\1',
14
+ /^eins\s/ => 'eine ',
15
+ /einsund/ => 'einund',
16
+ 'einszehn' => 'elf',
17
+ 'zweizehn' => 'zwölf',
18
+ 'sechszehn' => 'sechzehn',
19
+ 'siebenzehn' => 'siebzehn',
20
+ 'zweizig' => 'zwanzig',
21
+ 'dreizig' => 'dreißig',
22
+ 'sechszig' => 'sechzig',
23
+ 'siebenzig' => 'siebzig'
8
24
  }.freeze
9
25
 
26
+ attr_reader :number
27
+
10
28
  def initialize(number)
11
29
  @number = number.to_i
12
30
  end
13
31
 
14
32
  def parse
15
- return WORDS[:digits][@number] if @number < 10 && @number >= 0
33
+ return 'null' if number == 0
16
34
 
17
- number_string = @number.to_s.reverse.scan(/.{1,3}/).map.with_index{ |number_part, index|
18
- parse_triple(number_part.reverse.to_i, index)
19
- }.reverse.join
35
+ triples = split_into_reverse_triples(number)
20
36
 
21
- number_string.sub! /^und/, '' # TODO investigate
37
+ word = triples.each_with_index.reduce('') do |result, (triple, index)|
38
+ triple_word = triple_to_word(triple, index)
39
+ result.prepend(triple_word)
40
+ end.strip
22
41
 
23
- if @number < 0
24
- "minus #{number_string}"
25
- else
26
- number_string
27
- end
42
+ number < 0 ? "minus #{word}" : word
28
43
  end
29
44
 
30
- def parse_triple(number, triple_index)
31
- temp_number = number.abs
32
- number_word = ""
33
- temp_tens = ""
45
+ private
34
46
 
35
- return 'eins' if temp_number == 1 && triple_index == 0 # FIXME
36
-
37
- while temp_number > 0
38
- decimal_power = Math.log10(temp_number).floor
39
- number_base = 10 ** decimal_power
40
- number_tail = temp_number - (temp_number % number_base)
41
- digit = number_tail / number_base
47
+ def triples_count
48
+ @triples_count ||= split_into_reverse_triples(number).count
49
+ end
42
50
 
43
- copula = ((digit > 1) ? WORDS[:copula] : "")
44
- leading_single = (triple_index >= 2 && digit == 1) ? WORDS[:tens][0].to_s : WORDS[:digits][digit].to_s
51
+ def split_into_reverse_triples(number)
52
+ @reverse_triples ||= number.abs.to_s.reverse.scan(/.{1,3}/).map(&:reverse)
53
+ end
45
54
 
46
- if decimal_power == 2
47
- number_word << WORDS[:digits][digit].to_s << WORDS[:exponents][0].to_s
48
- end
55
+ def triple_to_word(triple, triple_index)
56
+ hundred_digit, ten_digit, unit_digit = split_triple(triple)
49
57
 
50
- if decimal_power == 0 && !temp_tens.empty?
51
- number_word << WORDS[:digits][digit].to_s
52
- end
58
+ word = [
59
+ hundred(hundred_digit),
60
+ unit(unit_digit),
61
+ copula(unit_digit, ten_digit),
62
+ ten(ten_digit)
63
+ ].join
53
64
 
54
- if decimal_power == 0 && temp_tens.empty?
55
- number_word << copula << leading_single
56
- end
65
+ word = append_exponent_identifier(word, triple_index)
66
+ apply_exceptions(word)
67
+ end
57
68
 
58
- if decimal_power == 1
59
- temp_tens << copula << WORDS[:tens][digit].to_s
60
- end
69
+ # splits up a triple into hundreds, tens and unit position
70
+ def split_triple(triple)
71
+ triple.match(/\A(\d)??(\d)??(\d)\z/).captures.map(&:to_i)
72
+ end
61
73
 
62
- temp_number = temp_number - number_tail
63
- end
74
+ # returns the word for the given unit number
75
+ def unit(digit)
76
+ return '' if digit.zero?
77
+ UNITS[digit - 1]
78
+ end
64
79
 
65
- number_word << temp_tens
80
+ # returns the copula between unit position and tens
81
+ def copula(unit_digit, ten_digit)
82
+ 'und' if ten_digit > 1 && !unit_digit.zero?
83
+ end
66
84
 
67
- if triple_index > 0
68
- number_word << WORDS[:exponents][triple_index].to_s
85
+ # returns the word for the given tens digit
86
+ def ten(digit)
87
+ case digit
88
+ when 0 then ''
89
+ when 1 then 'zehn'
90
+ else unit(digit) + 'zig'
69
91
  end
92
+ end
70
93
 
71
- number_word = handle_exceptions(number_word)
72
- number_word = remove_wrong_leadings(number_word)
94
+ # returns the word for the given hundreds number
95
+ def hundred(digit)
96
+ case digit
97
+ when 0 then ''
98
+ else unit(digit) + 'hundert'
99
+ end
100
+ end
73
101
 
74
- number_word
102
+ # adds the exponent word to the triple word
103
+ # e.g. tausend for the second triple (index = 1)
104
+ # Million for the third triple (index = 2)
105
+ # Milliarde for the fourth triple (index = 3)
106
+ #
107
+ # indexes => PREFIXES index
108
+ # 2,3 => 1; 4,5 => 2; 6,7 => 3; ... : floored division by 2
109
+ # etc.
110
+ def append_exponent_identifier(word, index)
111
+ return word if word.empty? || index.zero? || triples_count == 1
112
+
113
+ if index == 1
114
+ word + PREFIXES[:units][0]
115
+ elsif index.even?
116
+ pluralize_if_plural(word + ' ' + (PREFIXES[:units][index / 2] + "llion ").capitalize)
117
+ elsif index.odd?
118
+ pluralize_if_plural(word + ' ' + (PREFIXES[:units][index / 2] + "lliarde ").capitalize)
119
+ end
75
120
  end
76
121
 
77
- def handle_exceptions(word)
78
- word = word.gsub('einzehn', 'elf')
79
- word = word.gsub('zweizehn', 'zwölf')
80
- word = word.gsub('sechszehn', 'sechzehn')
81
- word = word.gsub('siebenzehn', 'siebzehn')
122
+ # pluralizes exponent identifiers
123
+ def pluralize_if_plural(word)
124
+ word =~ /^eins / ? word : word.sub(/e? $/, 'en ')
82
125
  end
83
126
 
84
- def remove_wrong_leadings(word)
85
- word.gsub(/^(und|null)/, '')
127
+ # replaces all exceptions in the number word
128
+ def apply_exceptions(word)
129
+ EXCEPTIONS.each do |exception, replacement|
130
+ word.sub!(exception, replacement)
131
+ end
132
+
133
+ word
86
134
  end
87
-
135
+
88
136
  end
89
- end
137
+ end
@@ -1,5 +1,5 @@
1
1
  module Figures
2
- VERSION = "0.1.0".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
4
4
 
5
5
 
@@ -112,6 +112,10 @@ describe Figures do
112
112
  assert_equal 'einhunderteins', Figures.parse(101, :de)
113
113
  end
114
114
 
115
+ it 'parses 131' do
116
+ assert_equal 'einhunderteinunddreißig', Figures.parse(131, :de)
117
+ end
118
+
115
119
  it 'parses 199' do
116
120
  assert_equal 'einhundertneunundneunzig', Figures.parse(199, :de)
117
121
  end
@@ -141,15 +145,32 @@ describe Figures do
141
145
  end
142
146
 
143
147
  it 'parses 1_000_000' do
144
- fail 'figure out'
145
- assert_equal 'million', Figures.parse(1_000_000, :de)
148
+ assert_equal 'eine Million', Figures.parse(1_000_000, :de)
149
+ end
150
+
151
+ it 'parses 1_000_001' do
152
+ assert_equal 'eine Million eins', Figures.parse(1_000_001, :de)
146
153
  end
147
154
 
148
155
  it 'parses 2_000_000' do
149
- fail 'figure out'
150
- assert_equal 'zwei millionen', Figures.parse(1_000_000, :de)
156
+ assert_equal 'zwei Millionen', Figures.parse(2_000_000, :de)
157
+ end
158
+
159
+ it 'parses 21_000_000' do
160
+ assert_equal 'einundzwanzig Millionen', Figures.parse(21_000_000, :de)
161
+ end
162
+
163
+ it 'parses 1_000_000_000' do
164
+ assert_equal 'eine Milliarde', Figures.parse(1_000_000_000, :de)
165
+ end
166
+
167
+ it 'parses 2_000_000_000' do
168
+ assert_equal 'zwei Milliarden', Figures.parse(2_000_000_000, :de)
169
+ end
170
+
171
+ it 'parses 2_000_003_000_000' do
172
+ assert_equal 'zwei Billionen drei Millionen', Figures.parse(2_000_003_000_000, :de)
151
173
  end
152
174
  end
153
175
  end
154
176
  end
155
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: figures
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Götze
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-10-26 00:00:00.000000000 Z
12
+ date: 2015-11-01 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: German number words.
15
15
  email: paul.christoph.goetze@gmail.com
@@ -26,6 +26,7 @@ files:
26
26
  - Rakefile
27
27
  - figures.gemspec
28
28
  - lib/figures.rb
29
+ - lib/figures/.german.rb.swp
29
30
  - lib/figures/german.rb
30
31
  - lib/figures/version.rb
31
32
  - spec/figures_spec.rb
@@ -49,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
50
  version: '0'
50
51
  requirements: []
51
52
  rubyforge_project:
52
- rubygems_version: 2.4.5.1
53
+ rubygems_version: 2.4.6
53
54
  signing_key:
54
55
  specification_version: 4
55
56
  summary: German number words