File Coverage

blib/lib/Lingua/EU/Numbers.pm
Criterion Covered Total %
statement 87 88 98.8
branch 48 56 85.7
condition 43 54 79.6
subroutine 7 7 100.0
pod 2 4 50.0
total 187 209 89.4


line stmt bran cond sub pod time code
1             package Lingua::EU::Numbers;
2              
3 5     5   40810 use strict;
  5         13  
  5         270  
4 5     5   32 use Carp;
  5         9  
  5         676  
5 5         12793 use vars qw(
6             @EXPORT @EXPORT_OK @ISA $VERSION
7             %num2alpha
8 5     5   28 );
  5         12  
9             require Exporter;
10              
11             @ISA = qw(Exporter);
12             $VERSION = '0.03';
13             @EXPORT = qw(
14             &cardinal2alpha
15             &ordinal2alpha
16             );
17             @EXPORT_OK = qw(
18             %num2alpha
19             );
20              
21             # The Bask numeral system is vigesimal (base 20). So far, going to
22             # 999_999_999_999.
23              
24             %num2alpha = (
25             0 => 'zero',
26             1 => 'bat',
27             2 => 'bi',
28             3 => 'hiru',
29             4 => 'lau',
30             5 => 'bost',
31             6 => 'sei',
32             7 => 'zazpi',
33             8 => 'zortzi',
34             9 => 'bederatzi',
35             10 => 'hamar',
36             11 => 'hamaika',
37             12 => 'hamabi',
38             13 => 'hamahiru',
39             14 => 'hamalau',
40             15 => 'hamabost',
41             16 => 'hamasei',
42             17 => 'hamazazpi',
43             18 => 'hemezortzi',
44             19 => 'hemeretzi',
45             20 => 'hogei',
46             40 => 'berrogei',
47             60 => 'hirurogei',
48             80 => 'laurogei',
49             100 => 'ehun',
50             200 => 'berrehun',
51             300 => 'hirurehun',
52             400 => 'laurehun',
53             500 => 'bostehun',
54             600 => 'seiehun',
55             700 => 'zazpiehun',
56             800 => 'zortziehun',
57             900 => 'bederatziehun',
58             1000 => 'mila',
59             1000000 => 'milioi bat',
60             1000000000 => 'mila milioi'
61             );
62              
63             #Names for quantifiers, every block of 3 digits
64             #(thousands, millions, billions)
65             my %block2alpha = (
66             block1 => 'mila',
67             block2 => 'milioi',
68             block3 => 'mila milioi'
69             );
70              
71              
72             #This function accepts an integer (scalar) as a parameter and
73             #returns a string (array), which is its Bask cardinal equivalent.
74             #
75             sub cardinal2alpha {
76 318 50   318 1 100631 return undef unless defined $_[0];
77 318         437 my $orig_num = shift;
78 318         471 my @result = ();
79 318         338 my ( $thousands, $hundreds, $tens, $units );
80 318         388 my $num = $orig_num;
81              
82             #Input validation
83 318 100       1169 unless ( $num =~ /^\d+$/ ) {
84 5         909 carp "Entry $num not valid. Should be numeric characters only";
85 5         27 return undef;
86             }
87              
88 313 100 66     2218 if ( $num > 999_999_999_999 or $num < 0 ) {
89 1         204 carp "Entry $num not valid. Number should be an integer between 0 and 999,999,999,999";
90 1         7 return undef;
91             }
92              
93             #Handling special cases
94 312 100       564 return $num2alpha{0} if $num == 0;
95 310 100       909 return $num2alpha{$num} if $num2alpha{$num};
96              
97 240         370 my $len = length($num);
98              
99             #Main logic: cutting number by block of 3 digits
100 240         537 while ( $len > 3 ) {
101              
102 166         427 $num = reverse($num);
103              
104             #Dealing with the part off the block(s) of three
105 166         404 my $extra_digits = substr( $num, int( ( $len - 1 ) / 3 ) * 3 );
106 166         229 $extra_digits = reverse($extra_digits);
107 166 100       467 push ( @result, triple_digit_handling($extra_digits) )
108             unless $extra_digits == 1;
109              
110             #Adding name for the quantifier
111 166         347 my $quantif = 'block' . ( int( ( $len - 1 ) / 3 ) );
112 166 100       584 push ( @result, $block2alpha{$quantif} ) unless $num =~ /000$/;
113              
114             #Special case for 1 million: adding the term for "one"
115 166 100 66     468 push ( @result, $num2alpha{1} ) if $len == 7 && $extra_digits == 1;
116              
117             #Adding "eta" after millions (except when there's no thousand)
118 166         293 my $whats_left = substr( reverse($num), length($extra_digits) );
119 166 100 100     1302 if ( ( $len <= 8 and $len >= 7 )
      100        
      100        
120             && $whats_left != 0
121             && ( reverse($num) !~ /^[^0]000/ ) )
122             {
123 22         38 push ( @result, "eta" );
124             }
125              
126             #Adding 'eta' for hundreds, except when there are tens and/or units
127 166 100       323 if ( length($num) <= 6 ) {
128 78         420 ( $units, $tens, $hundreds, $thousands, my @rest ) =
129             split ( //, reverse($orig_num) );
130              
131 78 100 100     986 if ( ( $hundreds != 0 && $tens == 0 && $units == 0 )
      100        
      100        
      66        
      100        
      66        
      100        
      66        
      66        
      66        
132             || ( $hundreds == 0 && ( $tens || $units ) ) && $num !~ /^0/
133             || ( $thousands == 0 && $hundreds == 0 && ( $tens || $units ) )
134             )
135             {
136 36         84 push ( @result, "eta" );
137             }
138             }
139              
140             #Dealing with the remaining digits
141 166         265 $num = reverse($num);
142 166         257 $num = substr( $num, length($extra_digits) );
143 166         372 $len = length($num);
144              
145             } #end while len > 3
146              
147 240 50       567 if ( $len <= 3 ) {
148 240         419 push ( @result, triple_digit_handling($num) );
149 240         1148 return "@result";
150             }
151             }
152              
153              
154             #This function takes an integer (scalar) as a parameter, which is
155             #a 3-digit number or less, and returns a string (array), which is
156             #its Bask equivalent.
157             #
158             sub triple_digit_handling {
159 352     352 0 458 my $num = shift;
160 352         465 my @result = ();
161 352         479 my ( $hundreds, $tens, $units, @tens_n_units );
162              
163             #Handling exceptional cases
164 352 50 33     1666 return undef if $num > 999 || $num < 0;
165 352 100       708 return if $num == 0;
166 256 100       600 return $num2alpha{$num} if $num2alpha{$num};
167              
168 214         331 my $len = length($num);
169              
170             #Handling 2-digit numbers
171 214 100       399 if ( $len == 2 ) {
172 152         765 ( $tens, $units ) = split ( //, sprintf( "%02d", $num ) );
173 152         314 @result = double_digit_handling( $tens, $units );
174 152         434 return @result;
175             }
176              
177             #Handling 3-digit numbers
178 62 50       150 if ( $len == 3 ) {
179 62         306 ( $hundreds, $tens, $units ) = split ( //, sprintf( "%03d", $num ) );
180 62 100       161 unless ( $hundreds == 0 ) {
181 28         38 $hundreds *= 100;
182 28         66 push ( @result, $num2alpha{$hundreds} );
183 28 50 66     138 push ( @result, "eta" ) if $tens || $units;
184             }
185              
186 62         123 @tens_n_units = double_digit_handling( $tens, $units );
187 62         101 push ( @result, @tens_n_units );
188 62         161 return @result;
189             }
190              
191             }
192              
193              
194             #This function takes two integers (scalars) as parameters (tens and units)
195             #and returns a string (array), which is their Bask equivalent.
196             #
197             sub double_digit_handling {
198 214     214 0 269 my $diz = shift;
199 214         273 my $unit = shift;
200 214         335 my $num = "$diz$unit";
201 214         390 my @result;
202              
203             #Handling exceptional cases
204 214 50       452 return if $num == 0;
205              
206 214 100       473 return $num2alpha{$num} if $num2alpha{$num};
207              
208 192 100       410 return $num2alpha{$unit} unless $diz;
209              
210             #Dealing with base 20
211 154 100       2951 if ( $diz =~ /[3579]/ ) {
212 80         119 $diz -= 1;
213 80         123 $unit += 10;
214             }
215 154         219 $diz = $diz * 10;
216              
217 154 50       283 if ($unit) { push ( @result, "$num2alpha{$diz}ta" ); }
  154         421  
218 0         0 else { push ( @result, $num2alpha{$diz} ); }
219 154         294 push ( @result, $num2alpha{$unit} );
220              
221 154         496 return @result;
222             }
223              
224              
225             #This function accepts an integer (scalar) as a parameter and
226             #returns a string (array), which is its Bask ordinal equivalent.
227             #
228             sub ordinal2alpha {
229 162 50   162 1 88031 return undef unless defined $_[0];
230 162         208 my $num = shift;
231 162         216 my @result;
232              
233             #Handling special cases
234 162 100       601 return undef unless $num =~ /^\d+$/;
235 157 100 66     862 return undef if ( $num < 0 || $num > 999_999_999_999 );
236 156 100       295 return "lehenengo" if $num == 1;
237              
238 155         289 push ( @result, join ( '', cardinal2alpha($num), "garren" ) );
239 155         645 return "@result";
240             }
241              
242             1;
243             __END__