File Coverage

lib/Convert/Number/Ethiopic.pm
Criterion Covered Total %
statement 82 86 95.3
branch 31 34 91.1
condition 9 13 69.2
subroutine 11 11 100.0
pod 0 3 0.0
total 133 147 90.4


line stmt bran cond sub pod time code
1             package Convert::Number::Ethiopic;
2              
3 1     1   6483 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   26 use strict;
  1         1  
  1         35  
9 1     1   4 use vars qw($VERSION @ENumbers %ENumbers);
  1         6  
  1         200  
10              
11 1     1   2 $VERSION = "0.16";
12              
13 1         15 require 5.000;
14              
15 1         5 @ENumbers =(
16             "፩", "፪", "፫", "፬", "፭", "፮", "፯", "፰", "፱",
17             "፲", "፳", "፴", "፵", "፶", "፷", "፸", "፹", "፺",
18             "፻", "፼"
19             );
20 1         668 %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   241 my ($self, $number) = @_;
49              
50 190 50       407 if ( $#_ > 1 ) {
51 0         0 warn ( "too many arguments." );
52 0         0 return;
53             }
54 190 50 66     1155 unless ( $number =~ /^\d+$/ || $number =~ /^[፩-፼]+$/ ) {
55 0         0 warn ( "'$number' is not a number." );
56 0         0 return;
57             }
58              
59 190         325 $self->{number} = $number;
60              
61 190         485 1;
62             }
63              
64              
65             sub new
66             {
67 1     1 0 154 my $class = shift;
68 1         2 my $self = {};
69              
70              
71 1         3 my $blessing = bless ( $self, $class );
72              
73 1         6 $self->{number} = undef;
74              
75 1 50 0     4 $self->_setArgs ( @_ ) || return if ( @_ );
76              
77 1         3 $blessing;
78             }
79              
80              
81             sub _fromEthiopic
82             {
83              
84             #
85             # just return if its a single char
86             #
87 95 100   95   331 return ( $ENumbers{$_[0]->{number}} ) if ( length($_[0]->{number}) == 1);
88              
89              
90 86         138 $_ = $_[0]->{number};
91              
92             #
93             # tack on a ፩ to avoid special condition check
94             #
95 86         298 s/^([፻፼])/፩$1/o;
96 86         164 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         327 s/([፻፼])([፩-፱])/$1."0$2"/oge; # add 0 if tens place empty
  53         196  
105 86         269 s/([፲-፺])([^፩-፱])/$1."0$2"/oge; # add 0 if ones place empty
  29         117  
106 86         306 s/([፲-፺])\b/$1."0"/oe; # repeat at end of string
  25         66  
107              
108              
109             # pad 0s for meto
110             #
111             # s/(፻)$/$1."00"/e; # this is stupid but tricks perl 5.6 into working
112 86         279 s/፻\b/፻00/o;
113              
114             # pad 0s for ilf
115             #
116 86         251 s/፼\b/፼0000/o;
117 86         137 s/፼፼/፼0000፼/og; # since /g doesn't work the first time..
118 86         106 s/፼፼/፼0000፼/og; # ...we do it again!
119 86         152 s/፻፼/፼00፼/og;
120 86         167 s/፼0([፩-፱])፼/፼000$1፼/og;
121 86         142 s/፼0([፩-፱])\b/፼000$1/o; # repeat at end of string
122 86         148 s/፼([፲-፺]0)፼/፼00$1፼/og;
123 86         181 s/፼([፲-፺]0)\b/፼00$1/o; # repeat at end of string
124 86         205 s/፼([፩-፺]{2})፼/፼00$1፼/og;
125 86         179 s/፼([፩-፺]{2})\b/፼00$1/o; # repeat at end of string
126              
127 86         547 s/[፻፼]//og;
128              
129             # fold tens:
130             #
131 1     1   5 tr/፲-፺/፩-፱/;
  1         2  
  1         11  
  86         262  
132              
133             # translit digits:
134             #
135 86         191 tr/፩-፱/1-9/;
136              
137 86         248 int $_;
138             }
139              
140              
141             sub _toEthiopic
142             {
143 95     95   175 my $number = $_[0]->{number};
144              
145 95         132 my $n = length ( $number ) - 1;
146              
147             # map and return a single digit number
148             # don't waste time with the loop:
149 95 100       176 return ( $ENumbers[$number-1] ) unless ( $n );
150              
151              
152 91 100       172 unless ( $n % 2 ) {
153             #
154             # Add dummy leading 0 to precondition the number for
155             # the algorithm and reduce one logic test within the
156             # for loop
157             #
158 50         83 $number = "0$number";
159 50         68 $n++;
160             }
161              
162 91         398 my @aNumberString = split ( //, $number );
163 91         133 my $eNumberString = "";
164              
165              
166             #
167             # read number from most to least significant digits:
168             #
169 91         202 for ( my $place = $n; $place >= 0; $place-- ) {
170             #
171             # initialize values to emptiness:
172             #
173 316         390 my ($aTen, $aOne) = ( 0, 0); # ascii ten's and one's place
174 316         401 my ($eTen, $eOne) = ('',''); # ethiopic ten's and one's place
175              
176              
177             #
178             # populate our tens and ones places from the number string:
179             #
180 316         398 $aTen = $aNumberString[$n-$place]; $place--;
  316         296  
181 316         327 $aOne = $aNumberString[$n-$place];
182 316 100       670 $eTen = $ENumbers[$aTen-1+9] if ( $aTen );
183 316 100       607 $eOne = $ENumbers[$aOne-1] if ( $aOne );
184              
185              
186             #
187             # pos tracks our 'pos'ition in a sequence of 4 digits
188             # to help determine what separator we need between
189             # a grouping of tens and ones.
190             #
191 316         430 my $pos = int ( $place % 4 ) / 2; # make even/odd
192              
193              
194             #
195             # find a separator, if any, to follow ethiopic ten and one:
196             #
197 316 100 100     882 my $sep
    100          
    100          
198             = ( $place )
199             ? ( $pos ) # odd
200             ? ( ($eTen ne '') || ($eOne ne '') )
201             ? '፻'
202             : ''
203             : '፼'
204             : ''
205             ;
206              
207              
208             #
209             # if $eOne is an Ethiopic '፩' we want to clear it under
210             # under special conditions. These ellision rules could be
211             # combined into a single big test but gets harder to read
212             # and manage:
213             #
214             # if ( ( $eOne eq '፩' ) && ( $eTen eq '' ) && ( $n > 1 ) ) {
215 316 100 100     883 if ( ( $eOne eq '፩' ) && ( $eTen eq '' ) ) {
216 72 100       170 if ( $sep eq '፻' ) {
    100          
217             #
218             # A superflous implied ፩ before ፻
219             #
220 29         39 $eOne = '';
221             }
222             elsif ( ($place+1) == $n ) { # recover from initial $place--
223             #
224             # ፩ is the leading digit.
225             #
226 23         29 $eOne = '';
227             }
228             }
229              
230              
231             #
232             # put it all together and append to our output number:
233             #
234 316         1100 $eNumberString .= "$eTen$eOne$sep";
235             }
236              
237 91         345 $eNumberString;
238             }
239              
240              
241             sub convert
242             {
243 190     190 0 567 my $self = shift;
244              
245              
246             #
247             # reset string if we've been passed one:
248             #
249 190 100       438 $self->number ( @_ ) if ( @_ );
250              
251 190 100       311 ( $self->number =~ /^[0-9]+$/ )
252             ? $self->_toEthiopic
253             : $self->_fromEthiopic
254             ;
255             }
256              
257              
258             sub number
259             {
260 380     380 0 10073 my $self = shift;
261              
262 380 100 50     836 $self->_setArgs ( @_ ) || return
263             if ( @_ );
264              
265 380         1426 $self->{number};
266             }
267              
268              
269             #########################################################
270             # Do not change this, Do not put anything below this.
271             # File must return "true" value at termination
272             1;
273             ##########################################################
274              
275              
276             __END__