File Coverage

lib/Convert/Number/Ethiopic.pm
Criterion Covered Total %
statement 88 99 88.8
branch 37 46 80.4
condition 9 13 69.2
subroutine 11 11 100.0
pod 0 3 0.0
total 145 172 84.3


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