roman_numbers 0.0.5 → 0.0.6
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 +4 -4
- data/lib/roman_numbers/roman_number.rb +50 -48
- data/lib/roman_numbers/version.rb +1 -1
- data/spec/integer_spec.rb +13 -0
- data/spec/roman_numbers/roman_number_spec.rb +32 -23
- data/spec/string_spec.rb +13 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19a16ed12f582cd4cf0facf87dfb249da2090d44
|
4
|
+
data.tar.gz: 6e46b7f767a96b8aa9e75f3800211edb22c93dd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ea20220716f648aa0c48f623e24fcbd6933ba4c3d9f3cb91c20d143a0f8e2e2b3b27e39439daf48e99c4173d1f7b02d67b4dfc7f2b5bc563ae976399f43e31b
|
7
|
+
data.tar.gz: 64a0f5f5bacf1c0542f11172758d36d0adc1ee6e46aee5794c171aab0d81b20224ea4ff00e81f7e7aef52791bbbeab3eeca0fab49333e7850aefd50a3183ee3b
|
@@ -43,28 +43,29 @@ module RomanNumbers
|
|
43
43
|
@output_roman = String.new
|
44
44
|
when String
|
45
45
|
@input_string = input.upcase
|
46
|
-
@
|
46
|
+
@output_integer = 0
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
attr_accessor :input_integer, :input_string, :output_roman, :staged_roman_hash, :output_integer
|
51
|
+
|
50
52
|
# converts arabic to roman
|
51
|
-
def convert_decimal_to_roman(input_integer
|
53
|
+
def convert_decimal_to_roman(passed_integer=input_integer)
|
52
54
|
# validating input
|
53
|
-
unless (1..3999).include?
|
54
|
-
raise InvalidInputError, "Invalid Input: #{
|
55
|
+
unless (1..3999).include? passed_integer
|
56
|
+
raise InvalidInputError, "Invalid Input: #{passed_integer}"
|
55
57
|
end
|
56
58
|
# getting staged roman hash
|
57
|
-
calculate_staged_roman_hash(
|
59
|
+
calculate_staged_roman_hash(passed_integer)
|
58
60
|
# extracting hash from staged roman hash
|
59
|
-
|
60
|
-
|
61
|
+
staged_roman_hash.each do |element|
|
62
|
+
output_roman << (element[:largest_element][:unit].to_s)*(element[:times])
|
61
63
|
end
|
62
|
-
|
64
|
+
output_roman
|
63
65
|
end
|
64
66
|
|
65
67
|
# converts given arabic number (in string form) to corresponding integer
|
66
|
-
def convert_roman_to_decimal(
|
67
|
-
input_roman
|
68
|
+
def convert_roman_to_decimal(passed_roman=input_string.clone)
|
68
69
|
# generating regex expressions
|
69
70
|
double_units_array = ROMAN_NON_REPEATABLE_UNITS_2.map { |element| ('^' + element[:unit].to_s) }
|
70
71
|
single_units_array = (ROMAN_REPEATABLE_UNITS + ROMAN_NON_REPEATABLE_UNITS_1).map { |element| ('^' + element[:unit].to_s) }
|
@@ -72,94 +73,95 @@ module RomanNumbers
|
|
72
73
|
single_units_regex = Regexp.new(single_units_array.join('|'))
|
73
74
|
# validation
|
74
75
|
# TODO: to add more validations
|
75
|
-
if
|
76
|
-
raise InvalidInputError, "Invalid Input: #{
|
76
|
+
if passed_roman =~ /(.)\1{#{MAX_ALLOWED_REPETITION},}/
|
77
|
+
raise InvalidInputError, "Invalid Input: #{passed_roman}"
|
77
78
|
end
|
78
79
|
# processing
|
79
|
-
if
|
80
|
-
if unit =
|
81
|
-
|
82
|
-
convert_roman_to_decimal(
|
83
|
-
elsif unit =
|
84
|
-
|
85
|
-
convert_roman_to_decimal(
|
80
|
+
if passed_roman.length > 0
|
81
|
+
if unit = passed_roman.slice!(double_units_regex)
|
82
|
+
self.output_integer += ROMAN_DOUBLE_UNITS.find { |element| element[:unit] == unit.to_sym }[:value]
|
83
|
+
convert_roman_to_decimal(passed_roman)
|
84
|
+
elsif unit = passed_roman.slice!(single_units_regex)
|
85
|
+
self.output_integer += ROMAN_SINGLE_UNITS.find { |element| element[:unit] == unit.to_sym }[:value]
|
86
|
+
convert_roman_to_decimal(passed_roman)
|
86
87
|
else
|
87
|
-
#
|
88
|
-
raise InvalidInputError, "Invalid Input: #{
|
88
|
+
# invalid input
|
89
|
+
raise InvalidInputError, "Invalid Input: #{passed_roman}"
|
89
90
|
end
|
90
91
|
else
|
91
92
|
# process is complete
|
92
|
-
|
93
|
+
self.output_integer
|
93
94
|
end
|
94
|
-
|
95
95
|
end
|
96
96
|
|
97
|
+
protected :input_integer, :input_string, :output_roman, :staged_roman_hash, :output_integer
|
98
|
+
|
97
99
|
private
|
98
100
|
|
99
101
|
# returns an array of hashed containing info on desired output roman
|
100
|
-
def calculate_staged_roman_hash(
|
102
|
+
def calculate_staged_roman_hash(passed_input_integer)
|
101
103
|
begin
|
102
|
-
temp_hash = largest_repeatable_element(
|
104
|
+
temp_hash = largest_repeatable_element(passed_input_integer)
|
103
105
|
rescue StartsWithNonRepeatableRomanUnitError => ex
|
104
|
-
temp_hash = largest_non_repeatable_element(
|
106
|
+
temp_hash = largest_non_repeatable_element(passed_input_integer)
|
105
107
|
end
|
106
108
|
if temp_hash
|
107
|
-
|
108
|
-
|
109
|
-
calculate_staged_roman_hash(
|
109
|
+
staged_roman_hash << temp_hash
|
110
|
+
passed_input_integer = temp_hash[:reduced_integer]
|
111
|
+
calculate_staged_roman_hash(passed_input_integer)
|
110
112
|
else
|
111
113
|
# processing done
|
112
|
-
|
114
|
+
staged_roman_hash
|
113
115
|
end
|
114
116
|
end
|
115
117
|
|
116
118
|
# returns reduced_integer, largest repeatable element, and number of times it can be repeated
|
117
|
-
def largest_repeatable_element(
|
118
|
-
if
|
119
|
-
largest_element = ROMAN_REPEATABLE_UNITS.find { |element|
|
119
|
+
def largest_repeatable_element(passed_input_integer)
|
120
|
+
if passed_input_integer > 0
|
121
|
+
largest_element = ROMAN_REPEATABLE_UNITS.find { |element| passed_input_integer >= element[:value] }
|
120
122
|
# TODO: make it efficient by removing elements before largest_element
|
121
123
|
# TODO: to use binary search instead
|
122
124
|
if largest_element
|
123
|
-
times =
|
124
|
-
reduced_integer =
|
125
|
+
times = passed_input_integer/largest_element[:value]
|
126
|
+
reduced_integer = passed_input_integer%largest_element[:value]
|
125
127
|
if times > MAX_ALLOWED_REPETITION
|
126
128
|
# given integer starts with non_repeatable roman unit
|
127
129
|
raise StartsWithNonRepeatableRomanUnitError
|
128
130
|
end
|
129
|
-
|
131
|
+
{:reduced_integer => reduced_integer, :largest_element => largest_element, :times => times}
|
130
132
|
else
|
131
133
|
# non-reachable code
|
132
134
|
raise NonReachableCodeError, 'LargestElementIsNil'
|
133
135
|
end
|
134
|
-
elsif
|
136
|
+
elsif passed_input_integer == 0
|
135
137
|
# process completed
|
136
|
-
|
138
|
+
nil
|
137
139
|
else
|
138
140
|
# non-reachable code
|
139
|
-
#
|
141
|
+
# passed_input_integer has to be >=0
|
140
142
|
raise NonReachableCodeError, 'ReceivedNegativeInteger'
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
144
146
|
# returns largest non-repeatable element
|
145
|
-
def largest_non_repeatable_element(
|
146
|
-
if
|
147
|
-
largest_element = ROMAN_NON_REPEATABLE_UNITS.find { |element|
|
147
|
+
def largest_non_repeatable_element(passed_input_integer)
|
148
|
+
if passed_input_integer > 0
|
149
|
+
largest_element = ROMAN_NON_REPEATABLE_UNITS.find { |element| passed_input_integer >= element[:value] }
|
148
150
|
# TODO: make it efficient by removing elements before largest_element
|
149
151
|
# TODO: to use binary search instead
|
150
152
|
if largest_element
|
151
|
-
reduced_integer =
|
152
|
-
|
153
|
+
reduced_integer = passed_input_integer%largest_element[:value]
|
154
|
+
{:reduced_integer => reduced_integer, :largest_element => largest_element, :times => 1}
|
153
155
|
else
|
154
156
|
# no non_repeatable element preset, but process is not complete yet
|
155
|
-
|
157
|
+
{:reduced_integer => passed_input_integer, :largest_element => nil, :times => 0}
|
156
158
|
end
|
157
|
-
elsif
|
159
|
+
elsif passed_input_integer == 0
|
158
160
|
# process completed
|
159
|
-
|
161
|
+
nil
|
160
162
|
else
|
161
163
|
# non-reachable code
|
162
|
-
#
|
164
|
+
# passed_input_integer has to be >=0
|
163
165
|
raise NonReachableCodeError, 'ReceivedNegativeInteger'
|
164
166
|
end
|
165
167
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
include RomanNumbers
|
3
|
+
|
4
|
+
describe Integer do
|
5
|
+
|
6
|
+
context "#to_roman" do
|
7
|
+
|
8
|
+
it "is added and internally calls 'convert_decimal_to_roman'" do
|
9
|
+
expect_any_instance_of(RomanNumbers::RomanNumber).to receive(:convert_decimal_to_roman)
|
10
|
+
7999112.to_roman
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -10,15 +10,40 @@ module RomanNumbers
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe '#convert_decimal_to_roman' do
|
13
|
-
shared_examples_for "
|
13
|
+
shared_examples_for "works_with_valid_integral_input_and_roman_output_collection" do |array|
|
14
14
|
# Note: here array is array of hashes
|
15
15
|
array.each do |hsh|
|
16
|
-
it %Q{returns #{hsh[:
|
16
|
+
it %Q{returns #{hsh[:roman]} for #{hsh[:integer]} as input} do
|
17
17
|
RomanNumber.new(hsh[:integer]).convert_decimal_to_roman.should == hsh[:roman]
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
|
+
context 'For Following Valid Inputs' do
|
23
|
+
it_behaves_like "works_with_valid_integral_input_and_roman_output_collection", Helpers.valid_inputs
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'For Following Invalid Input' do
|
27
|
+
[4000, 0, -10, '10'].each do |exp_input|
|
28
|
+
it %Q{raises #{$invalid_input_error} for #{exp_input} as input} do
|
29
|
+
expect {
|
30
|
+
RomanNumber.new(exp_input).convert_decimal_to_roman
|
31
|
+
}.to raise_error($invalid_input_error)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#convert_roman_to_decimal' do
|
38
|
+
shared_examples_for "works_with_valid_roman_input_and_integral_output_collection" do |array|
|
39
|
+
# Note: here array is array of hashes
|
40
|
+
array.each do |hsh|
|
41
|
+
it %Q{returns #{hsh[:integer]} for #{hsh[:roman]} as input} do
|
42
|
+
RomanNumber.new(hsh[:roman]).convert_roman_to_decimal.should == hsh[:integer]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
22
47
|
shared_examples_for "raises_error_on_invalid_roman_input_collection" do |array|
|
23
48
|
# Note: here array is array of integers
|
24
49
|
array.each do |integral_elem|
|
@@ -26,7 +51,7 @@ module RomanNumbers
|
|
26
51
|
expect {
|
27
52
|
RomanNumber.new(integral_elem).convert_decimal_to_roman
|
28
53
|
}.to raise_error($invalid_input_error)
|
29
|
-
|
54
|
+
end
|
30
55
|
end
|
31
56
|
end
|
32
57
|
|
@@ -35,17 +60,17 @@ module RomanNumbers
|
|
35
60
|
end
|
36
61
|
|
37
62
|
context %q(Symbols 'I', 'X', 'C', and 'M' can be repeated 4 times if 3rd and 4th are separated by smaller value) do
|
38
|
-
it_behaves_like "
|
63
|
+
it_behaves_like "works_with_valid_roman_input_and_integral_output_collection", Helpers.input_set_1
|
39
64
|
end
|
40
65
|
|
41
66
|
context %q(Symbols 'D', 'L', 'V' can never be repeated in succession) do
|
42
67
|
it_behaves_like "raises_error_on_invalid_roman_input_collection", %w(DD DDC LL LLX VV VVI)
|
43
68
|
end
|
44
69
|
|
45
|
-
context %Q(Symbol 'I' can be
|
70
|
+
context %Q(Symbol 'I' can be subtracted from 'V' and 'X', and
|
46
71
|
Symbol 'L' can be subtracted from 'L' and 'C', and
|
47
72
|
Symbol 'C' can be subtracted from 'D' and 'M') do
|
48
|
-
it_behaves_like "
|
73
|
+
it_behaves_like "works_with_valid_roman_input_and_integral_output_collection", Helpers.input_set_2
|
49
74
|
end
|
50
75
|
|
51
76
|
context %Q(Symbol 'I' can be subtrated from 'V' and 'X' only, and
|
@@ -53,23 +78,7 @@ module RomanNumbers
|
|
53
78
|
Symbol 'C' can be subtracted from 'D' and 'M' only) do
|
54
79
|
it_behaves_like "raises_error_on_invalid_roman_input_collection", %w(IL IC ID IM XD XM)
|
55
80
|
end
|
56
|
-
|
57
|
-
context 'For Following Valid Inputs' do
|
58
|
-
it_behaves_like "works_with_valid_integral_roman_input_collection", Helpers.valid_inputs
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'For Following Invalid Input' do
|
62
|
-
[4000, 0, -10, '10'].each do |exp_input|
|
63
|
-
it %Q{raises #{$invalid_input_error} for #{exp_input} as input} do
|
64
|
-
expect {
|
65
|
-
RomanNumber.new(exp_input).convert_decimal_to_roman
|
66
|
-
}.to raise_error($invalid_input_error)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
81
|
|
72
|
-
describe '#convert_roman_to_decimal' do
|
73
82
|
context 'For Valid Input' do
|
74
83
|
Helpers.valid_inputs.each do |hsh|
|
75
84
|
it %Q{returns #{hsh[:roman]} for #{hsh[:integer]} as input} do
|
data/spec/string_spec.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
include RomanNumbers
|
3
|
+
|
4
|
+
describe String do
|
5
|
+
|
6
|
+
context "#from_roman_to_integer" do
|
7
|
+
|
8
|
+
it "is added and internally calls 'convert_roman_to_decimal'" do
|
9
|
+
expect_any_instance_of(RomanNumbers::RomanNumber).to receive(:convert_roman_to_decimal)
|
10
|
+
"some_string".from_roman_to_integer
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roman_numbers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Munish Goyal
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,8 +113,10 @@ files:
|
|
113
113
|
- lib/roman_numbers/version.rb
|
114
114
|
- roman_numbers.gemspec
|
115
115
|
- spec/helpers.rb
|
116
|
+
- spec/integer_spec.rb
|
116
117
|
- spec/roman_numbers/roman_number_spec.rb
|
117
118
|
- spec/spec_helper.rb
|
119
|
+
- spec/string_spec.rb
|
118
120
|
homepage: https://github.com/goyalmunish/roman_numbers
|
119
121
|
licenses:
|
120
122
|
- MIT
|
@@ -142,5 +144,7 @@ summary: Conversion between Integers and Roman Numbers
|
|
142
144
|
test_files:
|
143
145
|
- autotest/discover.rb
|
144
146
|
- spec/helpers.rb
|
147
|
+
- spec/integer_spec.rb
|
145
148
|
- spec/roman_numbers/roman_number_spec.rb
|
146
149
|
- spec/spec_helper.rb
|
150
|
+
- spec/string_spec.rb
|