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