File Coverage

lib/Convert/Number/Roman.pm
Criterion Covered Total %
statement 105 135 77.7
branch 47 64 73.4
condition 3 9 33.3
subroutine 11 14 78.5
pod 0 6 0.0
total 166 228 72.8


line stmt bran cond sub pod time code
1             package Convert::Number::Roman;
2              
3 1     1   5983 use utf8; # can't find a way to conditionally load this with
  1         2  
  1         6  
4             # the scope applying throughout
5              
6             BEGIN
7             {
8 1     1   26 use strict;
  1         2  
  1         35  
9 1     1   5 use vars qw($VERSION %RomanDigits);
  1         5  
  1         125  
10              
11 1     1   2 $VERSION = '0.01';
12              
13 1         14 require 5.000;
14              
15 1         429 %RomanDigits =(
16             Ⅰ => 1,
17             Ⅱ => 2,
18             Ⅲ => 3,
19             Ⅳ => 4,
20             Ⅴ => 5,
21             Ⅵ => 6,
22             Ⅶ => 7,
23             Ⅷ => 8,
24             Ⅸ => 9,
25             Ⅹ => 10,
26             Ⅺ => 11,
27             Ⅻ => 12,
28             Ⅼ => 50,
29             Ⅽ => 100,
30             Ⅾ => 500,
31             Ⅿ => 1000,
32             ↁ => 5000,
33             ↂ => 10000
34             );
35             }
36              
37              
38             sub _setArgs
39             {
40 190     190   284 my ($self, $number, $style) = @_;
41              
42 190 50       385 if ( $#_ > 2 ) {
43 0         0 warn ( "too many arguments." );
44 0         0 return;
45             }
46 190 50       708 if ( $number =~ /lower|upper/ ) {
47 0         0 $style = $number;
48 0         0 $number = undef;
49             }
50 190 50       329 if ( $number ) {
51 190 50 66     1385 unless ( $number =~ /^\d+$/ || $number =~ /^[̄̿Ⅰ-ↂ]+$/ ) {
52 0         0 warn ( "'$number' is not a number." );
53 0         0 return;
54             }
55             else {
56 190         308 $self->{number} = $number;
57             }
58             }
59 190 50       310 if ( $style ) {
60 0 0       0 if ( $style =~ /lower|upper/i ) {
61 0         0 $self->{style} = lc($style);
62             }
63             else {
64 0         0 warn ( "'$style' is not a supported style, using 'upper'." );
65             }
66             }
67            
68              
69              
70 190         379 1;
71             }
72              
73              
74             sub new
75             {
76 1     1 0 118 my $class = shift;
77 1         2 my $self = {};
78              
79              
80 1         4 my $blessing = bless ( $self, $class );
81              
82 1         4 $self->{number} = undef;
83 1         3 $self->{style} = "upper";
84              
85 1 50 0     3 $self->_setArgs ( @_ ) || return if ( @_ );
86              
87 1         3 $blessing;
88             }
89              
90              
91             sub _fromRoman
92             {
93              
94 95     95   328 $_ = $_[0]->{number};
95             #
96             # convert to uppercase roman:
97             #
98 1     1   5 tr/ⅰ-ⅿ/Ⅰ-Ⅿ/; # get on up!
  1         1  
  1         10  
  95         286  
99             #
100             # just return if its a single char:
101             #
102 95 100       255 return ( $RomanDigits{$_} ) if ( length($_) == 1);
103             #
104             # make into math:
105             #
106 84         210 s/̿/̄̄/og;
107 84         498 while ( /[Ⅰ-ↂ](̄+)/ ) {
108 88         222 my $power = $1;
109 88         81 my $group;
110 88         1245 s/([Ⅰ-ↂ])$power/$group .= $1; "$1$power";/eg;
  151         229  
  151         452  
111 88         1835 s/([Ⅰ-ↂ]$power)+/($group)*($power)/;
112             }
113 84         259 s/̄/*1000/og;
114 84         132 s/Ⅿↂ/+9000/og;
115 84         109 s/Ⅿↁ/+4000/og;
116 84         106 s/ⅭⅯ/+900/og;
117 84         105 s/ⅭⅮ/+400/og;
118 84         104 s/ⅩⅭ/+90/og;
119 84         105 s/ⅩⅬ/+40/og;
120 84         270 s/([ↁⅮⅬ])/+$RomanDigits{$1}/og;
121 84         909 s/([ↂⅯⅭⅩ])/+$RomanDigits{$1}/og;
122 84         503 s/([Ⅰ-Ⅻ])/+$RomanDigits{$1}/og;
123 84         446 s/\([+*]/(/g;
124 84         181 s/\)\(/\)+\(/g;
125 84         254 s/\(([\d+]+)\)/eval"$1"/eg;
  127         3859  
126 84         279 s/^\+//;
127             #
128             # evaluate the expression:
129             #
130 84         4601 eval "$_";
131             }
132              
133              
134             sub _toRoman
135             {
136 219     219   289 my ($self, $number) = @_;
137 219 100       437 $number = $self->{number} unless ( defined($number) );
138              
139 219         305 $number =~ s/^0+//; # strip leading zeros
140              
141 219         220 my $roman;
142 219 100       539 if ( $number >= 40000 ) {
143 54         53 my $lines;
144 54         99 while ( $number ) {
145 162         524 $number =~ s/(\d{1,3})$//;
146 162         277 my $group = $1;
147 162 100       352 if ( $group != /^0+$/ ) {
148 124 100       181 if ( $lines ) {
149 88         167 my $rGroup = $self->_toRoman ( $group );
150 88         488 $rGroup =~ s/(.)/$1$lines/g;
151 88 100       242 $roman = ( $roman ) ? "$rGroup$roman" : $rGroup;
152             }
153             else {
154             # first cycle
155 36         65 $roman = $self->_toRoman ( $group );
156              
157             }
158             }
159 162         342 $lines .= "̄";
160             }
161              
162 54         254 $roman =~ s/̄̄/̿/g;
163              
164             } else {
165 165         359 while ( $number ) {
166 360 100       2060 if ( $number >= 10000 ) {
    50          
    50          
    50          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
    100          
    100          
    100          
167 16         21 $roman .= "ↂ";
168 16         33 $number -= 10000;
169             }
170             elsif ( $number >= 9000 ) {
171 0         0 $roman .= "Ⅿↂ";
172 0         0 $number -= 9000;
173             }
174             elsif ( $number >= 5000 ) {
175 0         0 $roman .= "ↁ";
176 0         0 $number -= 5000;
177             }
178             elsif ( $number >= 4000 ) {
179 0         0 $roman .= "Ⅿↁ";
180 0         0 $number -= 4000;
181             }
182             elsif ( $number >= 1000 ) {
183 30         41 $roman .= "Ⅿ";
184 30         66 $number -= 1000;
185             }
186             elsif ( $number >= 900 ) {
187 0         0 $roman .= "ⅭⅯ";
188 0         0 $number -= 900;
189             }
190             elsif ( $number >= 500 ) {
191 4         7 $roman .= "Ⅾ";
192 4         7 $number -= 500;
193             }
194             elsif ( $number >= 400 ) {
195 0         0 $roman .= "ⅭⅮ";
196 0         0 $number -= 400;
197             }
198             elsif ( $number >= 100 ) {
199 128         153 $roman .= "Ⅽ";
200 128         270 $number -= 100;
201             }
202             elsif ( $number >= 90 ) {
203 0         0 $roman .= "ⅩⅭ";
204 0         0 $number -= 90;
205             }
206             elsif ( $number >= 50 ) {
207 4         6 $roman .= "Ⅼ";
208 4         7 $number -= 50;
209             }
210             elsif ( $number >= 40 ) {
211 1         2 $roman .= "ⅩⅬ";
212 1         2 $number -= 40;
213             }
214             elsif ( $number > 12 ) {
215 43         53 $roman .= "Ⅹ";
216 43         90 $number -= 10;
217             }
218             elsif ( $number >= 10 ) {
219 64         70 $number -= 10;
220 64         139 $number =~ tr/0-2/Ⅹ-Ⅻ/;
221 64         71 $roman .= $number;
222 64         131 $number = 0;
223             }
224             else {
225 70         170 $number =~ tr/1-9/Ⅰ-Ⅸ/;
226 70         82 $roman .= $number;
227 70         176 $number = 0;
228             }
229             }
230             }
231              
232 219         512 $roman;
233             }
234              
235              
236             sub upperRoman
237             {
238 0     0 0 0 my ( $self, $roman ) = @_;
239              
240 0         0 $roman =~ tr/ⅰ-ⅿ/Ⅰ-Ⅿ/;
241 0         0 $roman;
242             }
243              
244              
245             sub lowerRoman
246             {
247 0     0 0 0 my ( $self, $roman ) = @_;
248              
249 0         0 $roman =~ tr/Ⅰ-Ⅿ/ⅰ-ⅿ/;
250 0         0 $roman;
251             }
252              
253              
254             sub convert
255             {
256 190     190 0 522 my $self = shift;
257              
258             #
259             # reset string if we've been passed one:
260             #
261 190 100       427 $self->_setArgs ( @_ ) if ( @_ );
262              
263 190 100       337 my $roman
264             = ( $self->number =~ /^[0-9]+$/ )
265             ? $self->_toRoman
266             : $self->_fromRoman
267             ;
268              
269 190 50       1577 ( $self->{style} eq "upper" )
270             ? $roman
271             : $self->lowerRoman ( $roman )
272             ;
273             }
274              
275              
276             sub number
277             {
278 285     285 0 6736 my $self = shift;
279              
280 285 100 50     1073 $self->_setArgs ( @_ ) || return
281             if ( @_ );
282              
283 285         1301 $self->{number};
284             }
285              
286              
287             sub style
288             {
289 0     0 0   my $self = shift;
290              
291 0 0 0       $self->_setArgs ( @_ ) || return
292             if ( @_ );
293              
294 0           $self->{style};
295             }
296              
297              
298             #########################################################
299             # Do not change this, Do not put anything below this.
300             # File must return "true" value at termination
301             1;
302             ##########################################################
303              
304              
305             __END__