cousin_roman 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -8
- data/cousin_roman.gemspec +3 -3
- data/lib/cousin_roman/arabian.rb +69 -0
- data/lib/cousin_roman/integer_extension.rb +11 -0
- data/lib/cousin_roman/{conversion.rb → roman.rb} +7 -37
- data/lib/cousin_roman/string_extension.rb +5 -5
- data/lib/cousin_roman/version.rb +1 -1
- data/lib/cousin_roman.rb +39 -1
- data/spec/cousin_roman/arabian_spec.rb +53 -0
- data/spec/cousin_roman/roman_spec.rb +182 -0
- data/spec/cousin_roman_spec.rb +13 -182
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f16dc19c0864ea51d64a45d94b4a0acf07cea72b
|
4
|
+
data.tar.gz: d0bd841b1ab1c55cb9ed88ef4671a9d9a134820c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec7600e60f73656ed7aefa6c1bdaf6a36e70d29943becb1f2d9115cf20f43eb7cc052228d85ad08d74c85e2d996ef5a82bfb395efe46bbadcfaecd7720643ea6
|
7
|
+
data.tar.gz: 9e41c6a9c0f41a90d47258623b6e8088dff534add7ff06ef8d69f2303cbf01c8c00de59bf5f31f4162d513a434eee36838ce7ebeafc6d56817746333c5727283
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# CousinRoman
|
2
2
|
|
3
|
-
`CousinRoman`
|
4
|
-
|
5
|
-
|
3
|
+
`CousinRoman` provides functionality to convert between Roman and Arabian numerals.
|
4
|
+
|
5
|
+
The functionality provided via String and Integer extensions.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -20,14 +20,22 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
Just use `String#
|
23
|
+
Just use `String#to_arabian` or `String#to_arabian!` methods
|
24
|
+
if you want to convert from Roman to Arabian
|
25
|
+
and
|
26
|
+
`Integer#to_roman` or `Integer#to_roman!`
|
27
|
+
if you want to convert from Arabian to Roman.
|
24
28
|
|
25
29
|
For example:
|
26
30
|
|
27
31
|
```ruby
|
28
|
-
'MMXIII'.
|
29
|
-
'MMYUOX'.
|
30
|
-
'MMYOUX'.
|
32
|
+
'MMXIII'.to_arabian # => 2013
|
33
|
+
'MMYUOX'.to_arabian # => nil
|
34
|
+
'MMYOUX'.to_arabian! # => TypeError: not a valid roman number
|
35
|
+
|
36
|
+
2013.to_roman # => 'MMXIII'
|
37
|
+
'0'.to_roman # => nil
|
38
|
+
'100500'.to_roman # => TypeError: not a valid roman number
|
31
39
|
```
|
32
40
|
|
33
41
|
## Testing
|
@@ -36,7 +44,7 @@ Just run
|
|
36
44
|
|
37
45
|
$ rake
|
38
46
|
|
39
|
-
under gem folder.
|
47
|
+
under gem folder (be prepared for massive output).
|
40
48
|
|
41
49
|
## Contributing
|
42
50
|
|
data/cousin_roman.gemspec
CHANGED
@@ -8,14 +8,14 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = CousinRoman::VERSION
|
9
9
|
spec.authors = ["Artem Pyanykh"]
|
10
10
|
spec.email = ["artem.pyanykh@gmail.com"]
|
11
|
-
spec.description = %q{Easily convert Roman
|
12
|
-
spec.summary = %q{Convert Roman numerals to Integers}
|
11
|
+
spec.description = %q{Easily convert Roman and Arabian number. Functionality provided via String and extensions}
|
12
|
+
spec.summary = %q{Convert Roman numerals to Integers and vice versa}
|
13
13
|
spec.homepage = "https://github.com/ArtemPyanykh/cousin_roman"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)*/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'cousin_roman'
|
2
|
+
|
3
|
+
module CousinRoman
|
4
|
+
module Arabian
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def valid?(number)
|
8
|
+
number.is_a? Integer and number >= 1 and number <= 3999
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_roman(arabian)
|
12
|
+
convert arabian if valid? arabian
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_roman!(arabian)
|
16
|
+
if valid? arabian
|
17
|
+
convert arabian
|
18
|
+
else
|
19
|
+
raise TypeError, 'not a valid roman number'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def convert(number)
|
24
|
+
thousands = number / 1000
|
25
|
+
hundreds = number / 100 % 10
|
26
|
+
tens = number / 10 % 10
|
27
|
+
ones = number % 10
|
28
|
+
|
29
|
+
build_roman(thousands, hundreds, tens, ones).upcase
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_roman(thousands, hundreds, tens, ones)
|
33
|
+
build_pow(thousands, 4) +
|
34
|
+
build_pow(hundreds, 3) +
|
35
|
+
build_pow(tens, 2) +
|
36
|
+
build_pow(ones, 1)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_pow(number, pow)
|
40
|
+
literals = pow_literals(pow)
|
41
|
+
|
42
|
+
case number
|
43
|
+
when 1..3 then literals[:one]*number
|
44
|
+
when 4 then literals[:subtractives][4]
|
45
|
+
when 5..8 then literals[:five] + (literals[:one]*(number - 5))
|
46
|
+
when 9 then literals[:subtractives][9]
|
47
|
+
else ""
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def pow_literals(pow)
|
52
|
+
one_literal, _ = ONES.find do |literal, value|
|
53
|
+
value >= 10 ** (pow - 1) and value < 10 ** pow
|
54
|
+
end
|
55
|
+
five_literal, _ = FIVES.find do |literal, value|
|
56
|
+
value >= 10 ** (pow - 1) and value < 10 ** pow
|
57
|
+
end
|
58
|
+
subtractive_literals = {}
|
59
|
+
SUBTRACTIVES.select do |literal, value|
|
60
|
+
value >= 10 ** (pow - 1) and value < 10 ** pow
|
61
|
+
end.each do |literal, number|
|
62
|
+
order = number / (10 ** (pow - 1))
|
63
|
+
subtractive_literals[order] = literal
|
64
|
+
end
|
65
|
+
|
66
|
+
{one: one_literal, five: five_literal, subtractives: subtractive_literals}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,39 +1,9 @@
|
|
1
|
-
|
2
|
-
LITERALS = [
|
3
|
-
'i', 'I',
|
4
|
-
'v', 'V',
|
5
|
-
'x', 'X',
|
6
|
-
'l', 'L',
|
7
|
-
'c', 'C',
|
8
|
-
'd', 'D',
|
9
|
-
'm', 'M'
|
10
|
-
]
|
11
|
-
|
12
|
-
ONES = {
|
13
|
-
'i' => 1,
|
14
|
-
'x' => 10,
|
15
|
-
'c' => 100,
|
16
|
-
'm' => 1000
|
17
|
-
}
|
18
|
-
|
19
|
-
FIVES = {
|
20
|
-
'v' => 5,
|
21
|
-
'l' => 50,
|
22
|
-
'd' => 500,
|
23
|
-
}
|
1
|
+
require 'cousin_roman'
|
24
2
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
'xl' => 40,
|
29
|
-
'xc' => 90,
|
30
|
-
'cd' => 400,
|
31
|
-
'cm' => 900,
|
32
|
-
}
|
33
|
-
|
34
|
-
FACTORS = ONES.merge(FIVES).merge(SUBTRACTIVE)
|
3
|
+
module CousinRoman
|
4
|
+
module Roman
|
5
|
+
extend self
|
35
6
|
|
36
|
-
class << self
|
37
7
|
def valid?(number)
|
38
8
|
matches = number.match roman_regex
|
39
9
|
matches and !matches[0].empty?
|
@@ -50,7 +20,7 @@ module CousinRoman
|
|
50
20
|
# 2. Sum this numeric values to get the final answer
|
51
21
|
def convert(number)
|
52
22
|
intermediate = number.dup.downcase
|
53
|
-
|
23
|
+
SUBTRACTIVES.each do |factor, value|
|
54
24
|
intermediate.gsub!(factor, "(#{value})")
|
55
25
|
end
|
56
26
|
ONES.merge(FIVES).each do |factor, value|
|
@@ -62,12 +32,12 @@ module CousinRoman
|
|
62
32
|
end
|
63
33
|
end
|
64
34
|
|
65
|
-
def
|
35
|
+
def to_arabian(number)
|
66
36
|
clean = number.strip
|
67
37
|
convert clean if valid? clean
|
68
38
|
end
|
69
39
|
|
70
|
-
def
|
40
|
+
def to_arabian!(number)
|
71
41
|
clean = number.strip
|
72
42
|
if valid? clean
|
73
43
|
convert clean
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require "cousin_roman/
|
1
|
+
require "cousin_roman/roman"
|
2
2
|
|
3
3
|
class String
|
4
|
-
def
|
5
|
-
CousinRoman.
|
4
|
+
def to_arabian
|
5
|
+
CousinRoman::Roman.to_arabian(self)
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
CousinRoman.
|
8
|
+
def to_arabian!
|
9
|
+
CousinRoman::Roman.to_arabian!(self)
|
10
10
|
end
|
11
11
|
end
|
data/lib/cousin_roman/version.rb
CHANGED
data/lib/cousin_roman.rb
CHANGED
@@ -1,3 +1,41 @@
|
|
1
1
|
require "cousin_roman/version"
|
2
|
-
require "cousin_roman/
|
2
|
+
require "cousin_roman/roman"
|
3
|
+
require "cousin_roman/arabian"
|
3
4
|
require "cousin_roman/string_extension"
|
5
|
+
require "cousin_roman/integer_extension"
|
6
|
+
|
7
|
+
module CousinRoman
|
8
|
+
LITERALS = [
|
9
|
+
'i', 'I',
|
10
|
+
'v', 'V',
|
11
|
+
'x', 'X',
|
12
|
+
'l', 'L',
|
13
|
+
'c', 'C',
|
14
|
+
'd', 'D',
|
15
|
+
'm', 'M'
|
16
|
+
]
|
17
|
+
|
18
|
+
ONES = {
|
19
|
+
'i' => 1,
|
20
|
+
'x' => 10,
|
21
|
+
'c' => 100,
|
22
|
+
'm' => 1000
|
23
|
+
}
|
24
|
+
|
25
|
+
FIVES = {
|
26
|
+
'v' => 5,
|
27
|
+
'l' => 50,
|
28
|
+
'd' => 500,
|
29
|
+
}
|
30
|
+
|
31
|
+
SUBTRACTIVES = {
|
32
|
+
'iv' => 4,
|
33
|
+
'ix' => 9,
|
34
|
+
'xl' => 40,
|
35
|
+
'xc' => 90,
|
36
|
+
'cd' => 400,
|
37
|
+
'cm' => 900,
|
38
|
+
}
|
39
|
+
|
40
|
+
FACTORS = ONES.merge(FIVES).merge(SUBTRACTIVES)
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CousinRoman::Arabian do
|
4
|
+
describe 'valid?' do
|
5
|
+
it 'should consider invalid numbers <= 0' do
|
6
|
+
(-10..0).each do |number|
|
7
|
+
CousinRoman::Arabian.valid?(number).should be_false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should consider valid numbers > 1 and <= 3999' do
|
12
|
+
CousinRoman::Arabian.valid?(1).should be_true
|
13
|
+
CousinRoman::Arabian.valid?(3999).should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should consider invalid numbers > 3999' do
|
17
|
+
CousinRoman::Arabian.valid?(4000).should be_false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
describe 'convert' do
|
23
|
+
1.upto(3999).each do |arabian|
|
24
|
+
it "should succeed on #{arabian}", slow: true do
|
25
|
+
roman = CousinRoman::Arabian.convert(arabian)
|
26
|
+
CousinRoman::Roman.valid?(roman).should be_true
|
27
|
+
CousinRoman::Roman.convert(roman).should == arabian
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'to_roman' do
|
33
|
+
it 'should call convert on valid number' do
|
34
|
+
CousinRoman::Arabian.should_receive(:convert).and_call_original
|
35
|
+
CousinRoman::Arabian.to_roman(1).should == 'I'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return nil on invalid number' do
|
39
|
+
CousinRoman::Arabian.to_roman(100500).should be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'to_roman!' do
|
44
|
+
it 'should call convert on valid number' do
|
45
|
+
CousinRoman::Arabian.should_receive(:convert).and_call_original
|
46
|
+
CousinRoman::Arabian.to_roman!(1).should == 'I'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should throw TypeError on invalid number' do
|
50
|
+
expect { CousinRoman::Arabian.to_roman!(100500) }.to raise_error(TypeError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CousinRoman::Roman do
|
4
|
+
describe 'valid?' do
|
5
|
+
describe 'literals validation' do
|
6
|
+
ASCII = 0.upto(127).map(&:chr)
|
7
|
+
NON_ROMAN_LITERALS = ASCII - CousinRoman::LITERALS
|
8
|
+
|
9
|
+
CousinRoman::LITERALS.each do |l|
|
10
|
+
it "should allow #{l}" do
|
11
|
+
CousinRoman::Roman.valid?(l).should be_true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
NON_ROMAN_LITERALS.each do |l|
|
16
|
+
it "should not allow #{l}" do
|
17
|
+
CousinRoman::Roman.valid?(l).should be_false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should not allow mix of allowed and disallowed literals' do
|
22
|
+
CousinRoman::Roman.valid?('MYCXI').should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should not allow empty string' do
|
26
|
+
CousinRoman::Roman.valid?('').should be_false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'factors validation' do
|
31
|
+
CousinRoman::ONES.keys.each do |factor|
|
32
|
+
1.upto(3).each do |reps|
|
33
|
+
it "should allow #{reps} repetitions of #{factor}" do
|
34
|
+
CousinRoman::Roman.valid?(factor*reps).should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not allow more than 3 repetitions of #{factor}" do
|
39
|
+
CousinRoman::Roman.valid?(factor*4).should be_false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
CousinRoman::FIVES.merge(CousinRoman::SUBTRACTIVES).keys.each do |factor|
|
44
|
+
it "should not allow repetition of #{factor}" do
|
45
|
+
CousinRoman::Roman.valid?(factor*2).should be_false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should allow ones and fives of the same power' do
|
50
|
+
CousinRoman::FIVES.keys.each_with_index do |five, index|
|
51
|
+
1.upto(3).each do |reps|
|
52
|
+
corresponding_one = CousinRoman::ONES.keys[index]
|
53
|
+
CousinRoman::Roman.valid?(five + (corresponding_one * reps)).should be_true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should not allow subtractives and ones or fives of the same power' do
|
59
|
+
CousinRoman::Roman.valid?('ivi').should be_false
|
60
|
+
CousinRoman::Roman.valid?('ixi').should be_false
|
61
|
+
CousinRoman::Roman.valid?('xlx').should be_false
|
62
|
+
CousinRoman::Roman.valid?('xcx').should be_false
|
63
|
+
CousinRoman::Roman.valid?('cdc').should be_false
|
64
|
+
CousinRoman::Roman.valid?('cmc').should be_false
|
65
|
+
|
66
|
+
CousinRoman::Roman.valid?('ivv').should be_false
|
67
|
+
CousinRoman::Roman.valid?('ixv').should be_false
|
68
|
+
CousinRoman::Roman.valid?('xll').should be_false
|
69
|
+
CousinRoman::Roman.valid?('xcl').should be_false
|
70
|
+
CousinRoman::Roman.valid?('cdd').should be_false
|
71
|
+
CousinRoman::Roman.valid?('cmd').should be_false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'ordering validation' do
|
76
|
+
context 'when validating that factors are ordered by descreasing of power' do
|
77
|
+
ORDERINGS_RIGHT = [
|
78
|
+
'md', 'mc',
|
79
|
+
'cl', 'cx',
|
80
|
+
'xv', 'xi',
|
81
|
+
'mcm', 'mcd',
|
82
|
+
'cxc', 'cxl',
|
83
|
+
'xix', 'xiv'
|
84
|
+
]
|
85
|
+
ORDERINGS_WRONG = [
|
86
|
+
'dm', 'lc', 'vx',
|
87
|
+
'cmm', 'cdm',
|
88
|
+
'xcc', 'xlc',
|
89
|
+
'ixx', 'ivx'
|
90
|
+
]
|
91
|
+
|
92
|
+
ORDERINGS_RIGHT.each do |pair|
|
93
|
+
high, low = pair.split('', 2)
|
94
|
+
it "should allow #{high}#{low}" do
|
95
|
+
CousinRoman::Roman.valid?(high + low).should be_true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
ORDERINGS_WRONG.each do |pair|
|
100
|
+
low, high = pair.split('', 2)
|
101
|
+
it "should not allow #{low}#{high}" do
|
102
|
+
CousinRoman::Roman.valid?(low + high).should be_false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'convert' do
|
110
|
+
context 'singular factors' do
|
111
|
+
CousinRoman::FACTORS.each do |factor, number|
|
112
|
+
it "should convert #{factor} to #{number}" do
|
113
|
+
CousinRoman::Roman.convert(factor).should == number
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'repetitive factors' do
|
119
|
+
CousinRoman::ONES.each do |factor, number|
|
120
|
+
1.upto(3).each do |reps|
|
121
|
+
it "should convert #{factor*reps} to #{number*reps}" do
|
122
|
+
CousinRoman::Roman.convert(factor*reps).should == (number*reps)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'different power factors' do
|
129
|
+
it 'should honor ones' do
|
130
|
+
CousinRoman::Roman.convert('mcxi').should == 1111
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should honor fives' do
|
134
|
+
CousinRoman::Roman.convert('dlv').should == 555
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should honor ones and fives together' do
|
138
|
+
CousinRoman::Roman.convert('mdclxvi').should == 1666
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should honor subtractives ones and fives together' do
|
142
|
+
CousinRoman::Roman.convert('mcmlxxxix').should == 1989
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should honor max number' do
|
146
|
+
CousinRoman::Roman.convert('mmmcmxcix').should == 3999
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should honor the longest number' do
|
150
|
+
CousinRoman::Roman.convert('dccclxxxviii').should == 888
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should be case insensitive' do
|
154
|
+
number = 'MCXI'
|
155
|
+
CousinRoman::Roman.convert(number).should == CousinRoman::Roman.convert(number.downcase)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe 'to_arabian' do
|
161
|
+
it 'should call convert on valid number' do
|
162
|
+
CousinRoman::Roman.should_receive(:convert).and_call_original
|
163
|
+
CousinRoman::Roman.to_arabian('MMM').should == 3000
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should return nil on invalid number' do
|
167
|
+
CousinRoman::Roman.to_arabian('YRU?!').should be_nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe 'to_arabian!' do
|
172
|
+
it 'should call convert on valid number' do
|
173
|
+
CousinRoman::Roman.should_receive(:convert).and_call_original
|
174
|
+
CousinRoman::Roman.to_arabian!('MMM').should == 3000
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should throw TypeError on invalid number' do
|
178
|
+
expect { CousinRoman::Roman.to_arabian!('YRU?!') }.to raise_error(TypeError)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
data/spec/cousin_roman_spec.rb
CHANGED
@@ -4,195 +4,26 @@ describe CousinRoman do
|
|
4
4
|
it 'should have a version number' do
|
5
5
|
CousinRoman::VERSION.should_not be_nil
|
6
6
|
end
|
7
|
-
|
8
|
-
describe 'valid?' do
|
9
|
-
describe 'literals validation' do
|
10
|
-
ASCII = 0.upto(127).map(&:chr)
|
11
|
-
NON_ROMAN_LITERALS = ASCII - CousinRoman::LITERALS
|
12
|
-
|
13
|
-
CousinRoman::LITERALS.each do |l|
|
14
|
-
it "should allow #{l}" do
|
15
|
-
CousinRoman.valid?(l).should be_true
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
NON_ROMAN_LITERALS.each do |l|
|
20
|
-
it "should not allow #{l}" do
|
21
|
-
CousinRoman.valid?(l).should be_false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should not allow mix of allowed and disallowed literals' do
|
26
|
-
CousinRoman.valid?('MYCXI').should be_false
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'should not allow empty string' do
|
30
|
-
CousinRoman.valid?('').should be_false
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe 'factors validation' do
|
35
|
-
CousinRoman::ONES.keys.each do |factor|
|
36
|
-
1.upto(3).each do |reps|
|
37
|
-
it "should allow #{reps} repetitions of #{factor}" do
|
38
|
-
CousinRoman.valid?(factor*reps).should be_true
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should not allow more than 3 repetitions of #{factor}" do
|
43
|
-
CousinRoman.valid?(factor*4).should be_false
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
CousinRoman::FIVES.merge(CousinRoman::SUBTRACTIVE).keys.each do |factor|
|
48
|
-
it "should not allow repetition of #{factor}" do
|
49
|
-
CousinRoman.valid?(factor*2).should be_false
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'should allow ones and fives of the same power' do
|
54
|
-
CousinRoman::FIVES.keys.each_with_index do |five, index|
|
55
|
-
1.upto(3).each do |reps|
|
56
|
-
corresponding_one = CousinRoman::ONES.keys[index]
|
57
|
-
CousinRoman.valid?(five + (corresponding_one * reps)).should be_true
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'should not allow subtractives and ones or fives of the same power' do
|
63
|
-
CousinRoman.valid?('ivi').should be_false
|
64
|
-
CousinRoman.valid?('ixi').should be_false
|
65
|
-
CousinRoman.valid?('xlx').should be_false
|
66
|
-
CousinRoman.valid?('xcx').should be_false
|
67
|
-
CousinRoman.valid?('cdc').should be_false
|
68
|
-
CousinRoman.valid?('cmc').should be_false
|
69
|
-
|
70
|
-
CousinRoman.valid?('ivv').should be_false
|
71
|
-
CousinRoman.valid?('ixv').should be_false
|
72
|
-
CousinRoman.valid?('xll').should be_false
|
73
|
-
CousinRoman.valid?('xcl').should be_false
|
74
|
-
CousinRoman.valid?('cdd').should be_false
|
75
|
-
CousinRoman.valid?('cmd').should be_false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
describe 'ordering validation' do
|
80
|
-
context 'when validating that factors are ordered by descreasing of power' do
|
81
|
-
ORDERINGS_RIGHT = [
|
82
|
-
'md', 'mc',
|
83
|
-
'cl', 'cx',
|
84
|
-
'xv', 'xi',
|
85
|
-
'mcm', 'mcd',
|
86
|
-
'cxc', 'cxl',
|
87
|
-
'xix', 'xiv'
|
88
|
-
]
|
89
|
-
ORDERINGS_WRONG = [
|
90
|
-
'dm', 'lc', 'vx',
|
91
|
-
'cmm', 'cdm',
|
92
|
-
'xcc', 'xlc',
|
93
|
-
'ixx', 'ivx'
|
94
|
-
]
|
95
|
-
|
96
|
-
ORDERINGS_RIGHT.each do |pair|
|
97
|
-
high, low = pair.split('', 2)
|
98
|
-
it "should allow #{high}#{low}" do
|
99
|
-
CousinRoman.valid?(high + low).should be_true
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
ORDERINGS_WRONG.each do |pair|
|
104
|
-
low, high = pair.split('', 2)
|
105
|
-
it "should not allow #{low}#{high}" do
|
106
|
-
CousinRoman.valid?(low + high).should be_false
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
describe 'convert' do
|
114
|
-
context 'singular factors' do
|
115
|
-
CousinRoman::FACTORS.each do |factor, number|
|
116
|
-
it "should convert #{factor} to #{number}" do
|
117
|
-
CousinRoman.convert(factor).should == number
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
context 'repetitive factors' do
|
123
|
-
CousinRoman::ONES.each do |factor, number|
|
124
|
-
1.upto(3).each do |reps|
|
125
|
-
it "should convert #{factor*reps} to #{number*reps}" do
|
126
|
-
CousinRoman.convert(factor*reps).should == (number*reps)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
context 'different power factors' do
|
133
|
-
it 'should honor ones' do
|
134
|
-
CousinRoman.convert('mcxi').should == 1111
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'should honor fives' do
|
138
|
-
CousinRoman.convert('dlv').should == 555
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'should honor ones and fives together' do
|
142
|
-
CousinRoman.convert('mdclxvi').should == 1666
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'should honor subtractives ones and fives together' do
|
146
|
-
CousinRoman.convert('mcmlxxxix').should == 1989
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'should honor max number' do
|
150
|
-
CousinRoman.convert('mmmcmxcix').should == 3999
|
151
|
-
end
|
152
|
-
|
153
|
-
it 'should honor the longest number' do
|
154
|
-
CousinRoman.convert('dccclxxxviii').should == 888
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'should be case insensitive' do
|
158
|
-
number = 'MCXI'
|
159
|
-
CousinRoman.convert(number).should == CousinRoman.convert(number.downcase)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
describe 'to_arabic' do
|
165
|
-
it 'should call convert on valid number' do
|
166
|
-
CousinRoman.should_receive(:convert).and_call_original
|
167
|
-
CousinRoman.to_arabic('MMM').should == 3000
|
168
|
-
end
|
169
|
-
|
170
|
-
it 'should return nil on invalid number' do
|
171
|
-
CousinRoman.to_arabic('YRU?!').should be_nil
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
describe 'to_arabic!' do
|
176
|
-
it 'should call convert on valid number' do
|
177
|
-
CousinRoman.should_receive(:convert).and_call_original
|
178
|
-
CousinRoman.to_arabic!('MMM').should == 3000
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'should throw TypeError on invalid number' do
|
182
|
-
expect { CousinRoman.to_arabic!('YRU?!') }.to raise_error(TypeError)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
7
|
end
|
187
8
|
|
188
9
|
describe String do
|
189
|
-
[:
|
10
|
+
[:to_arabian, :to_arabian!].each do |meth|
|
190
11
|
it { should respond_to(meth) }
|
191
12
|
|
192
|
-
it "should call CousinRoman.#{meth} on String##{meth}" do
|
13
|
+
it "should call CousinRoman::Roman.#{meth} on String##{meth}" do
|
193
14
|
roman = 'i'
|
194
|
-
CousinRoman.should_receive(meth).with(roman).and_call_original
|
15
|
+
CousinRoman::Roman.should_receive(meth).with(roman).and_call_original
|
195
16
|
roman.send(meth).should == 1
|
196
17
|
end
|
197
18
|
end
|
198
19
|
end
|
20
|
+
|
21
|
+
describe Integer do
|
22
|
+
[:to_roman, :to_roman!].each do |meth|
|
23
|
+
it "should call CousinRoman::Arabian.#{meth} on Fixnum##{meth}" do
|
24
|
+
arabian = 1
|
25
|
+
CousinRoman::Arabian.should_receive(meth).with(arabian).and_call_original
|
26
|
+
arabian.send(meth).should == 'I'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cousin_roman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Pyanykh
|
@@ -52,8 +52,8 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description: Easily convert Roman
|
56
|
-
|
55
|
+
description: Easily convert Roman and Arabian number. Functionality provided via String
|
56
|
+
and extensions
|
57
57
|
email:
|
58
58
|
- artem.pyanykh@gmail.com
|
59
59
|
executables: []
|
@@ -69,9 +69,13 @@ files:
|
|
69
69
|
- Rakefile
|
70
70
|
- cousin_roman.gemspec
|
71
71
|
- lib/cousin_roman.rb
|
72
|
-
- lib/cousin_roman/
|
72
|
+
- lib/cousin_roman/arabian.rb
|
73
|
+
- lib/cousin_roman/integer_extension.rb
|
74
|
+
- lib/cousin_roman/roman.rb
|
73
75
|
- lib/cousin_roman/string_extension.rb
|
74
76
|
- lib/cousin_roman/version.rb
|
77
|
+
- spec/cousin_roman/arabian_spec.rb
|
78
|
+
- spec/cousin_roman/roman_spec.rb
|
75
79
|
- spec/cousin_roman_spec.rb
|
76
80
|
- spec/spec_helper.rb
|
77
81
|
homepage: https://github.com/ArtemPyanykh/cousin_roman
|
@@ -97,7 +101,9 @@ rubyforge_project:
|
|
97
101
|
rubygems_version: 2.1.8
|
98
102
|
signing_key:
|
99
103
|
specification_version: 4
|
100
|
-
summary: Convert Roman numerals to Integers
|
104
|
+
summary: Convert Roman numerals to Integers and vice versa
|
101
105
|
test_files:
|
106
|
+
- spec/cousin_roman/arabian_spec.rb
|
107
|
+
- spec/cousin_roman/roman_spec.rb
|
102
108
|
- spec/cousin_roman_spec.rb
|
103
109
|
- spec/spec_helper.rb
|