File Coverage

blib/lib/Lingua/ITA/Numbers.pm
Criterion Covered Total %
statement 94 141 66.6
branch 30 62 48.3
condition 10 15 66.6
subroutine 15 22 68.1
pod 13 13 100.0
total 162 253 64.0


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1) -*-
2              
3             package Lingua::ITA::Numbers;
4             # ABSTRACT: Number 2 Word conversion in ITA.
5              
6 1     1   118370 use 5.16.0;
  1         4  
7 1     1   6 use utf8;
  1         2  
  1         16  
8 1     1   33 use warnings;
  1         1  
  1         216  
9              
10             # {{{ use block
11              
12             use overload (
13 1         15 '""' => \&get_string,
14             '0+' => \&get_number,
15             '=' => \&clone,
16             '+' => \&add,
17             '-' => \&minus,
18             '*' => \&mult,
19             '/' => \&div,
20 1     1   757 fallback => 1);
  1         2210  
21              
22 1     1   151 use Carp qw(carp);
  1         2  
  1         178  
23 1     1   684 use Export::Attrs;
  1         28410  
  1         16  
24              
25             # }}}
26             # {{{ variables declaration
27             our $VERSION = '0.2603250';
28              
29             our %SIGN_NAMES = ('-' => 'meno',
30             '+' => '');
31              
32             our %OUTPUT_DECIMAL_DELIMITERS = (normal => 'virgola',
33             fract => 'e');
34              
35             our %NUMBER_NAMES = (
36             0 => 'zero',
37             1 => 'un',
38             2 => 'due',
39             3 => 'tre',
40             4 => 'quattro',
41             5 => 'cinque',
42             6 => 'sei',
43             7 => 'sette',
44             8 => 'otto',
45             9 => 'nove',
46             10 => 'dieci',
47             11 => 'undici',
48             12 => 'dodici',
49             13 => 'tredici',
50             14 => 'quattordici',
51             15 => 'quindici',
52             16 => 'sedici',
53             17 => 'diciassette',
54             18 => 'diciotto',
55             19 => 'diciannove',
56             20 => 'venti',
57             30 => 'trenta',
58             40 => 'quaranta',
59             50 => 'cinquanta',
60             60 => 'sessanta',
61             70 => 'settanta',
62             80 => 'ottanta',
63             90 => 'novanta',
64             100 => 'cento');
65              
66             our @PART_NAMES = qw(_ mila milioni miliardi);
67             our @UNITS_NAMES = qw(_ mille milione miliardo);
68             our @FRACT_NAMES = qw(decim centesim millesim decimillesim centomillesim
69             milionesim decimilionesim centomilionesim
70             miliardesim);
71              
72             our %DEFAULT_OPTS = (
73             decimal => 0,
74             decmode => 'normal',
75             name => "",
76             );
77              
78             # }}}
79             # {{{ number_to_it
80              
81             sub number_to_it :Export {
82 17     17 1 210513 my ($number,$opts) = @_;
83 17 100       50 $opts = {} unless defined $opts;
84 17         76 $opts = {%DEFAULT_OPTS,%$opts};
85 17         39 my $parsed = parse_num_string($number);
86 17         33 my @parts = ();
87 17         46 push @parts,$SIGN_NAMES{$parsed->{sign}};
88 17         38 my $intpart = convert_to_string($parsed->{intpart});
89 17         31 my $one = $NUMBER_NAMES{1};
90 17         165 $intpart =~ s/($one)$/$1o/;
91 17         32 push @parts,$intpart;
92 17 50       39 if ($opts->{name}) {
93             my $name = ! ref($opts->{name}) ? $opts->{name} :
94 0 0       0 ($intpart eq $NUMBER_NAMES{1} ? $opts->{name}[0] : $opts->{name}[1]);
    0          
95              
96 0         0 push @parts,$name;
97             }
98              
99 17 50 33     78 if ($parsed->{fracpart} || $opts->{decimal}) {
100 0         0 push @parts,$OUTPUT_DECIMAL_DELIMITERS{$opts->{decmode}};
101 0 0       0 if ($opts->{decimal}) {
102 0 0       0 if (length($parsed->{fracpart}) < $opts->{decimal}) {
103             $parsed->{fracpart} .= "0" x ($opts->{decimal} -
104 0         0 length($parsed->{fracpart}));
105             }
106             }
107 0         0 my $fractpart = convert_to_string($parsed->{fracpart});
108 0 0       0 if ($opts->{decmode} eq 'fract') {
109 0 0       0 if ($fractpart eq $NUMBER_NAMES{1}) {
110 0         0 push @parts,$fractpart,$FRACT_NAMES[length($parsed->{fracpart}) - 1] . "o";
111             }
112             else {
113 0         0 push @parts,$fractpart,$FRACT_NAMES[length($parsed->{fracpart}) - 1] . "i";
114             }
115             }
116             else {
117 0         0 my $one = $NUMBER_NAMES{1};
118 0         0 $fractpart =~ s/($one)$/$1o/;
119 0         0 push @parts,$fractpart;
120             }
121             }
122 17         37 my $result = join(" ",@parts);
123 17         49 $result =~ s/^\s*//;
124 17         120 return $result;
125 1     1   1016 }
  1         2  
  1         12  
126              
127             # }}}
128             # {{{ convert_short
129              
130             sub convert_short {
131 1     1   1291 use integer;
  1         18  
  1         7  
132 22     22 1 35 my $num = shift; # 1 < num < 1000
133              
134 22         32 my $hundreds = $num / 100;
135 22         29 my $tens = $num % 100;
136 22         29 my @parts = ();
137 22 100       53 if ($hundreds == 1) {
    100          
138 5         10 push @parts,$NUMBER_NAMES{100};
139             }
140             elsif ($hundreds > 1) {
141 8         27 push @parts,$NUMBER_NAMES{$hundreds},$NUMBER_NAMES{100};
142             }
143 22 100       47 if ($tens == 0) {
    100          
144             #nothing
145             ;
146             }
147             elsif ($tens <= 20) {
148 6         14 push @parts,$NUMBER_NAMES{$tens};
149             }
150             else {
151 14         19 my $units = $tens % 10;
152 14         17 $tens = $tens - $units;
153 14         30 my $tenstr = $NUMBER_NAMES{$tens};
154 14 100 100     59 $tenstr =~ s/.$// if ($units == 1) or ($units == 8);
155 14         26 push @parts,$tenstr;
156 14 50       28 if ($units >= 1) {
157 14         29 push @parts,$NUMBER_NAMES{$units};
158             }
159             }
160 22         78 return join("",@parts);
161             }
162              
163             # }}}
164             # {{{ convert_to_string
165              
166             sub convert_to_string {
167 1     1   338 use integer;
  1         2  
  1         6  
168 17     17 1 25 my $number = shift; #$number >= 0 and integer
169 17 100 66     163 return $NUMBER_NAMES{0} if !$number || $number !~ m/[1-9]/;
170 16 100       51 return $NUMBER_NAMES{1} if "$number" eq "1" ;
171 14 100       37 if (my $r = length($number) % 3) {
172 9         26 $number = "0" x (3 - $r) . $number;
173             }
174 14         66 my @blocks = ($number =~ m!(\d\d\d)!g);
175 14         23 @blocks = reverse @blocks;
176 14 50       28 if (@blocks > 4) {
177 0         0 carp "Numbers bigger than 1e10-1 not handled in version $VERSION";
178 0         0 return;
179             }
180 14         20 my @name_parts = ();
181 14         19 my $firstpart = "";
182 14 100       48 if ($blocks[0] == 1) {
    50          
183             #nb one of the following blocks is != 0, since the whole number
184             #is greater than one
185 2         6 $firstpart = $NUMBER_NAMES{1};
186             }
187             elsif ($blocks[0] > 1) {
188 12         26 $firstpart = convert_short($blocks[0]);
189             }
190 14 50 66     68 if ($#blocks >= 1 && $blocks[1] == 1) {
    100 66        
191 0         0 $firstpart = $UNITS_NAMES[1] . $firstpart;
192             }
193             elsif ($#blocks >= 1 && $blocks[1] > 1) {
194 6         15 $firstpart = convert_short($blocks[1]) . $PART_NAMES[1] . $firstpart;
195             }
196 14         45 push @name_parts,$firstpart;
197 14         37 foreach my $pos (2..$#blocks) {
198 4 50       14 next unless $blocks[$pos];
199 4         7 push @name_parts," ";
200 4 50       14 if ($blocks[$pos] == 1) {
201 0         0 push @name_parts,$NUMBER_NAMES{1} . " " . $UNITS_NAMES[$pos];
202             }
203             else {
204 4         12 my $part = convert_short($blocks[$pos]);
205 4         15 push @name_parts,$part. " " . $PART_NAMES[$pos];
206             }
207             }
208 14         30 my $tmp = join("",reverse(@name_parts));
209 14         51 $tmp =~ s/^\s*//;
210 14         123 $tmp =~ s/\s*$//;
211 14         38 $tmp =~ s!\s+! !g;
212 14         36 return $tmp;
213             }
214              
215             # }}}
216             # {{{ parse_num_string
217              
218             sub parse_num_string {
219 17     17 1 28 my $string = shift;
220              
221 17         100 return { intpart => $string,
222             sign => '+',
223             fracpart => 0,
224             };
225             }
226              
227             # }}}
228              
229             # OO Methods
230             # {{{ new
231             sub new {
232 9     9 1 862 my $class = shift;
233 9         11 my $number = shift;
234 9         17 my @a = @_;
235 9         35 my %opts = (%DEFAULT_OPTS,@a);
236 9         46 return bless { number => $number,
237             opts => \%opts}, $class;
238             }
239              
240             # }}}
241             # {{{ get_string
242              
243             sub get_string {
244 9     9 1 41 my $self = shift;
245 9         57 return number_to_it($self->{number},$self->{opts});
246             }
247              
248             # }}}
249             # {{{ get_number
250              
251             sub get_number {
252 0     0 1   my $self = shift;
253             return $self->{number}
254 0           }
255              
256             # }}}
257             # {{{ set_number
258              
259             sub set_number {
260 0     0 1   my $self = shift;
261 0           $self->{number} = shift;
262 0           return $self;
263             }
264              
265             # }}}
266             # {{{ add
267              
268             sub add {
269 0     0 1   my $self = shift;
270 0           my $num = shift;
271 0 0         $num = UNIVERSAL::isa($num,__PACKAGE__) ? $num->{number} : $num;
272 0           my $tmp = $self->{number} + $num;
273             return bless {number => $tmp,
274 0           opts => $self->{opts}},ref($self);
275             }
276              
277             # }}}
278             # {{{ mult
279              
280             sub mult {
281 0     0 1   my $self = shift;
282 0           my $num = shift;
283 0 0         $num = UNIVERSAL::isa($num,__PACKAGE__) ? $num->{number} : $num;
284             return bless {number => $self->{number} * $num,
285 0           opts => $self->{opts}},ref($self);
286             }
287              
288             # }}}
289             # {{{ div
290              
291             sub div {
292 0     0 1   my $self = shift;
293 0           my $num = shift;
294 0 0         $num = UNIVERSAL::isa($num,__PACKAGE__) ? $num->{number} : $num;
295 0           my $inverted = shift;
296             my $tmp =
297 0 0         ($inverted) ? $num / $self->{number} : $self->{number} / $num;
298             return bless {number => $tmp,
299 0           opts => $self->{opts}},ref($self);
300             }
301              
302             # }}}
303             # {{{ minus
304              
305             sub minus {
306 0     0 1   my $self = shift;
307 0           my $num = shift;
308 0 0         $num = UNIVERSAL::isa($num,__PACKAGE__) ? $num->{number} : $num;
309 0           my $inverted = shift;
310             my $tmp =
311 0 0         ($inverted) ? $num - $self->{number} : $self->{number} - $num;
312             return bless {number => $tmp,
313 0           opts => $self->{opts}},ref($self);
314             }
315              
316             # }}}
317             # {{{ clone
318              
319             sub clone {
320 0     0 1   my $self = shift;
321 0           my $class = ref($self);
322 0           return bless {%$self},$class;
323             }
324              
325             # }}}
326              
327             1;
328             __END__