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