File Coverage

lib/Convert/Number/Ethiopic.pm
Criterion Covered Total %
statement 83 100 83.0
branch 32 36 88.8
condition 9 13 69.2
subroutine 11 11 100.0
pod 0 3 0.0
total 135 163 82.8


line stmt bran cond sub pod time code
1             package Convert::Number::Ethiopic;
2              
3 1     1   13542 use utf8; # can't find a way to conditionally load this with
  1         2  
  1         5  
4             # the scope applying throughout
5              
6             BEGIN
7             {
8 1     1   49 use strict;
  1         2  
  1         40  
9 1     1   4 use warnings;
  1         2  
  1         55  
10 1     1   15 use vars qw($VERSION @ENumbers %ENumbers);
  1         2  
  1         262  
11              
12 1     1   4 $VERSION = "0.21";
13              
14 1         16 require 5.000;
15              
16 1         8 @ENumbers =(
17             "፩", "፪", "፫", "፬", "፭", "፮", "፯", "፰", "፱",
18             "፲", "፳", "፴", "፵", "፶", "፷", "፸", "፹", "፺",
19             "፻", "፼"
20             );
21 1         2175 %ENumbers =(
22             '፩' => 1,
23             '፪' => 2,
24             '፫' => 3,
25             '፬' => 4,
26             '፭' => 5,
27             '፮' => 6,
28             '፯' => 7,
29             '፰' => 8,
30             '፱' => 9,
31             '፲' => 10,
32             '፳' => 20,
33             '፴' => 30,
34             '፵' => 40,
35             '፶' => 50,
36             '፷' => 60,
37             '፸' => 70,
38             '፹' => 80,
39             '፺' => 90,
40             '፻' => 100,
41             '፼' => 10000
42             );
43              
44             }
45              
46              
47             sub _setArgs
48             {
49 190     190   608 my ($self, $number) = @_;
50              
51 190 50       460 if ( $#_ > 1 ) {
52 0         0 warn ( "too many arguments." );
53 0         0 return;
54             }
55 190 50 66     1134 unless ( $number =~ /^\d+$/ || $number =~ /^[፩-፼]+$/ ) {
56 0         0 warn ( "'$number' is not a number." );
57 0         0 return;
58             }
59              
60 190         482 $self->{number} = $number;
61              
62 190         462 1;
63             }
64              
65              
66             sub new
67             {
68 1     1 0 129421 my $class = shift;
69 1         3 my $self = {};
70              
71              
72 1         3 my $blessing = bless ( $self, $class );
73              
74 1         9 $self->{number} = undef;
75              
76 1 50 0     6 $self->_setArgs ( @_ ) || return if ( @_ );
77              
78 1         4 $blessing;
79             }
80              
81              
82             sub _fromEthiopic
83             {
84              
85             #
86             # just return if its a single char
87             #
88 95 100   95   298 return ( $ENumbers{$_[0]->{number}} ) if ( length($_[0]->{number}) == 1);
89              
90              
91 86         210 $_ = $_[0]->{number};
92              
93             #
94             # tack on a ፩ to avoid special condition check
95             #
96 86         382 s/^([፻፼])/፩$1/o;
97 86         211 s/፼፻/፼፩፻/og;
98              
99             # what we do now is pad 0s around ፻ and ፼, these regexi try to kill
100             # two birds with one stone but could be split and simplified
101              
102             #
103             # pad 0 around ones and tens
104             #
105 86         302 s/([፻፼])([፩-፱])/$1."0$2"/oge; # add 0 if tens place empty
  53         236  
106 86         288 s/([፲-፺])([^፩-፱])/$1."0$2"/oge; # add 0 if ones place empty
  29         160  
107 86 50       179 if ( $] >= 5.012 ) {
108 86         266 s/([፲-፺])$/$1."0"/oe; # repeat at end of string
  7         25  
109              
110             # pad 0s for meto
111             #
112             # s/(፻)$/$1."00"/e; # this is stupid but tricks perl 5.6 into working
113 86         184 s/፻$/፻00/o;
114              
115             # pad 0s for ilf
116             #
117 86         191 s/፼$/፼0000/o;
118              
119             # pad 0s for ilf
120             #
121 86         153 s/፼፼/፼0000፼/og; # since /g doesn't work the first time..
122 86         149 s/፼፼/፼0000፼/og; # ...we do it again!
123 86         165 s/፻፼/፼00፼/og;
124 86         219 s/፼0([፩-፱])፼/፼000$1፼/og;
125              
126 86         205 s/፼0([፩-፱])$/፼000$1/o; # repeat at end of string
127              
128 86         196 s/፼([፲-፺]0)፼/፼00$1፼/og;
129              
130 86         186 s/፼([፲-፺]0)$/፼00$1/o; # repeat at end of string
131              
132 86         500 s/፼([፩-፺]{2})፼/፼00$1፼/og;
133              
134 86         212 s/፼([፩-፺]{2})$/፼00$1/o; # repeat at end of string
135             }
136             else { ##
137 0         0 s/([፲-፺])\b/$1."0"/oe; # repeat at end of string
  0         0  
138              
139             # pad 0s for meto
140             #
141             # s/(፻)$/$1."00"/e; # this is stupid but tricks perl 5.6 into working
142 0         0 s/፻\b$/፻00/o;
143              
144             # pad 0s for ilf
145             #
146 0         0 s/፼\b/፼0000/o;
147 0         0 s/፼፼/፼0000፼/og; # since /g doesn't work the first time..
148 0         0 s/፼፼/፼0000፼/og; # ...we do it again!
149 0         0 s/፻፼/፼00፼/og;
150 0         0 s/፼0([፩-፱])፼/፼000$1፼/og;
151              
152 0         0 s/፼0([፩-፱])\b/፼000$1/o; # repeat at end of string
153              
154 0         0 s/፼([፲-፺]0)፼/፼00$1፼/og;
155              
156 0         0 s/፼([፲-፺]0)\b/፼00$1/o; # repeat at end of string
157              
158 0         0 s/፼([፩-፺]{2})፼/፼00$1፼/og;
159              
160 0         0 s/፼([፩-፺]{2})\b/፼00$1/o; # repeat at end of string
161             }
162              
163 86         455 s/[፻፼]//og;
164              
165             # fold tens:
166             #
167 86         242 tr/፲-፺/፩-፱/;
168              
169             # translit digits:
170             #
171 86         190 tr/፩-፱/1-9/;
172              
173 86         283 int $_;
174             }
175              
176              
177             sub _toEthiopic
178             {
179 95     95   249 my $number = $_[0]->{number};
180              
181 95         156 my $n = length ( $number ) - 1;
182              
183             # map and return a single digit number
184             # don't waste time with the loop:
185 95 100       200 return ( $ENumbers[$number-1] ) unless ( $n );
186              
187              
188 91 100       210 unless ( $n % 2 ) {
189             #
190             # Add dummy leading 0 to precondition the number for
191             # the algorithm and reduce one logic test within the
192             # for loop
193             #
194 50         117 $number = "0$number";
195 50         122 $n++;
196             }
197              
198 91         371 my @aNumberString = split ( //, $number );
199 91         150 my $eNumberString = "";
200              
201              
202             #
203             # read number from most to least significant digits:
204             #
205 91         232 for ( my $place = $n; $place >= 0; $place-- ) {
206             #
207             # initialize values to emptiness:
208             #
209 316         676 my ($aTen, $aOne) = ( 0, 0); # ascii ten's and one's place
210 316         697 my ($eTen, $eOne) = ('',''); # ethiopic ten's and one's place
211              
212              
213             #
214             # populate our tens and ones places from the number string:
215             #
216 316         511 $aTen = $aNumberString[$n-$place]; $place--;
  316         461  
217 316         458 $aOne = $aNumberString[$n-$place];
218 316 100       705 $eTen = $ENumbers[$aTen-1+9] if ( $aTen );
219 316 100       848 $eOne = $ENumbers[$aOne-1] if ( $aOne );
220              
221              
222             #
223             # pos tracks our 'pos'ition in a sequence of 4 digits
224             # to help determine what separator we need between
225             # a grouping of tens and ones.
226             #
227 316         765 my $pos = int ( $place % 4 ) / 2; # make even/odd
228              
229              
230             #
231             # find a separator, if any, to follow ethiopic ten and one:
232             #
233 316 100 100     1045 my $sep
    100          
    100          
234             = ( $place )
235             ? ( $pos ) # odd
236             ? ( ($eTen ne '') || ($eOne ne '') )
237             ? '፻'
238             : ''
239             : '፼'
240             : ''
241             ;
242              
243              
244             #
245             # if $eOne is an Ethiopic '፩' we want to clear it under
246             # under special conditions. These ellision rules could be
247             # combined into a single big test but gets harder to read
248             # and manage:
249             #
250             # if ( ( $eOne eq '፩' ) && ( $eTen eq '' ) && ( $n > 1 ) ) {
251 316 100 100     888 if ( ( $eOne eq '፩' ) && ( $eTen eq '' ) ) {
252 72 100       170 if ( $sep eq '፻' ) {
    100          
253             #
254             # A superflous implied ፩ before ፻
255             #
256 29         48 $eOne = '';
257             }
258             elsif ( ($place+1) == $n ) { # recover from initial $place--
259             #
260             # ፩ is the leading digit.
261             #
262 23         37 $eOne = '';
263             }
264             }
265              
266              
267             #
268             # put it all together and append to our output number:
269             #
270 316         979 $eNumberString .= "$eTen$eOne$sep";
271             }
272              
273 91         370 $eNumberString;
274             }
275              
276              
277             sub convert
278             {
279 190     190 0 613 my $self = shift;
280              
281              
282             #
283             # reset string if we've been passed one:
284             #
285 190 100       502 $self->number ( @_ ) if ( @_ );
286              
287 190 100       357 ( $self->number =~ /^[0-9]+$/ )
288             ? $self->_toEthiopic
289             : $self->_fromEthiopic
290             ;
291             }
292              
293              
294             sub number
295             {
296 380     380 0 6928 my $self = shift;
297              
298 380 100 50     914 $self->_setArgs ( @_ ) || return
299             if ( @_ );
300              
301 380         1228 $self->{number};
302             }
303              
304              
305             #########################################################
306             # Do not change this, Do not put anything below this.
307             # File must return "true" value at termination
308             1;
309             ##########################################################
310              
311              
312             __END__