cousin_roman 0.0.2 → 1.0.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 +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
|