figures 0.1.0 → 0.2.0

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