File Coverage

blib/lib/Lingua/ITA/Numbers.pm
Criterion Covered Total %
statement 95 142 66.9
branch 30 62 48.3
condition 10 15 66.6
subroutine 15 22 68.1
pod 13 13 100.0
total 163 254 64.1


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