File Coverage

blib/lib/Lingua/pt_BR/Nums2Words.pm
Criterion Covered Total %
statement 73 73 100.0
branch 32 32 100.0
condition 8 8 100.0
subroutine 9 9 100.0
pod 1 1 100.0
total 123 123 100.0


line stmt bran cond sub pod time code
1             package Lingua::pt_BR::Nums2Words;
2              
3 1     1   63474 use strict;
  1         2  
  1         24  
4 1     1   4 use warnings;
  1         2  
  1         21  
5 1     1   4 use Exporter 'import';
  1         9  
  1         31  
6 1     1   5 use Carp;
  1         1  
  1         58  
7 1     1   5 use utf8;
  1         2  
  1         3  
8              
9             our $VERSION = '0.02';
10             our @EXPORT_OK = qw/num2word/;
11              
12             =encoding utf8
13              
14             =head1 NAME
15              
16             Lingua::pt_BR::Nums2Words - Takes a number and gives back its written
17             form in Brazilian Portuguese
18              
19             =head1 SYNOPSIS
20              
21             use Lingua::pt_BR::Nums2Words ('num2word');
22              
23             print num2word(91) # prints 'noventa e um'
24             print num2word('19') # prints 'dezenove'
25              
26             print num2word(1000) # prints 'mil'
27             print num2word(1001) # prints 'mil e um'
28             print num2word(1_001_001) # prints 'um milhão, mil e um'
29             print num2word(1_001_250) # prints 'um milhão, mil duzentos e cinquenta'
30              
31             =head1 DESCRIPTION
32              
33             Takes a number and gives back its written form in Brazilian
34             Portuguese.
35              
36             B: 1000 will produce 'mil', and not 'um mil'.
37              
38             =head1 METHODS
39              
40             =cut
41              
42             my %cardinals;
43              
44             $cardinals{units} = [undef, qw/um dois três quatro cinco seis sete oito
45             nove/];
46              
47             $cardinals{first_tens} = [undef, qw/onze doze treze quatorze quinze dezesseis
48             dezessete dezoito dezenove/];
49              
50             $cardinals{tens} = [undef, qw/dez vinte trinta quarenta cinquenta sessenta
51             setenta oitenta noventa/];
52              
53             $cardinals{hundreds} = [undef, qw/cento duzentos trezentos quatrocentos
54             quinhentos seiscentos setecentos oitocentos novecentos/];
55              
56             $cardinals{megas} = [undef, qw/mil milh bilh trilh quadrilh quintilh/];
57              
58             =head2 num2word( $number )
59              
60             Receives a number and returns it written in words.
61              
62             my $written_number = nums2words(991);
63             print $written_number # prints 'novecentos e noventa e um'
64              
65             =cut
66              
67             sub num2word {
68 38     38 1 2662 my $number = shift;
69 38 100       205 croak 'No argument provided' unless defined $number;
70 37 100       493 croak "Not a workable number: $number" unless $number =~ /^\d{1,19}$/x;
71 33 100       69 if ($number == 0) { return 'zero' }
  2         8  
72              
73 31         53 return _solve_triads( _make_triads($number) );
74             }
75              
76             =head1 INTERNALS
77              
78             These methods should not be used directly (unless you know what you're
79             doing). They are documented here just for the sake of completeness.
80              
81             =head2 _make_triads( $number )
82              
83             Receives a number, splits it in triads (according to the following
84             examples) and returns a list of triads. Examples: 123 turns to the
85             list (123). 12345 turns to the list (12, 345). 1234567 turns to the
86             list (1, 234, 567).
87              
88             =cut
89              
90             sub _make_triads {
91 31     31   42 my $number = shift;
92 31         37 my @triads;
93 31   100     75 my $offset = (length $number) % 3 || 3;
94              
95 31         86 while (my $triad = substr $number, 0, $offset, '') {
96 81         103 push @triads, $triad;
97 81 100       159 if ($offset != 3) { $offset = 3 }
  24         50  
98             }
99              
100 31         87 return @triads;
101             }
102              
103             =head2 _solve_triads( @triads )
104              
105             Receives a list of triads, calls the function _solve_triad in each of
106             them and apply the "megas" (millions, billions, trillions).
107              
108             =cut
109              
110             sub _solve_triads {
111 31     31   67 my @triads = @_;
112 31         40 my $megas_counter = $#triads;
113 31         39 my @triads_str;
114              
115 31         49 for my $triad (@triads) {
116 81 100       152 if ($triad == 0) {
117 15         17 $megas_counter--;
118 15         21 next;
119             }
120              
121 66         93 my $triad_str = _solve_triad($triad);
122              
123 66 100       104 if ($megas_counter > 0) {
124 36         47 my $mega = $cardinals{megas}->[$megas_counter];
125 36 100       55 if ($megas_counter > 1) { $mega .= $triad == 1 ? 'ão' : 'ões' }
  20 100       34  
126 36         60 $triad_str .= " $mega";
127              
128 36 100       59 if ($triad_str eq 'um mil') { $triad_str = 'mil' }
  6         10  
129              
130 36         42 $megas_counter--;
131             }
132              
133 66         98 push @triads_str, $triad_str;
134             }
135              
136 31         34 my $resp_str;
137              
138 31 100       63 if (@triads_str == 1) {
139 10         13 $resp_str = $triads_str[0];
140             }
141             else {
142 21         57 $resp_str .= join ', ', @triads_str[0 .. $#triads_str - 1];
143              
144 21         34 my $last_triad = $triads[-1];
145 21         24 my $last_triad_str;
146              
147 21 100 100     60 if ($last_triad % 100 == 0 || $last_triad < 100) {
148 13         18 $last_triad_str = "e $triads_str[-1]";
149             }
150             else {
151 8         11 $last_triad_str = $triads_str[-1];
152             }
153              
154 21         38 $resp_str .= " $last_triad_str";
155             }
156              
157 31         148 return $resp_str;
158             }
159              
160             =head2 _solve_triad( $number )
161              
162             Receives a number with one to three digits (a triad) and returns it
163             written in words.
164              
165             =cut
166              
167             sub _solve_triad {
168 66     66   86 my $number = shift;
169              
170 66 100       104 if ($number == 100) { return 'cem' }
  1         2  
171              
172 65         149 my $padded_number = sprintf "%03d", $number;
173 65         154 my ($hundreds, $tens, $units) = split '', $padded_number;
174              
175 65         89 my @resp;
176              
177 65 100       89 if ($hundreds) { push @resp, $cardinals{hundreds}->[$hundreds] }
  21         37  
178              
179 65         94 my $first_tens = $tens . $units;
180              
181 65 100 100     141 if ($first_tens > 10 and $first_tens < 20) {
182 2         5 push @resp, $cardinals{first_tens}->[$units];
183             }
184             else {
185 63 100       90 if ($tens) { push @resp, $cardinals{tens}->[$tens] }
  8         14  
186 63 100       86 if ($units) { push @resp, $cardinals{units}->[$units] }
  48         79  
187             }
188              
189 65         159 return join ' e ', @resp;
190             }
191              
192             1;
193              
194             =head1 SEE ALSO
195              
196             Lingua::PT::Nums2Words for pt_PT Portuguese.
197              
198             =head1 AUTHOR
199              
200             Gil Magno Egils@cpan.orgE
201              
202             =head1 THANKS TO
203              
204             Italo Gonçales (cpan:GONCALES) Eitalo.goncales@gmail.comE
205              
206             =head1 COPYRIGHT AND LICENSE
207              
208             Copyright (C) 2015 by Gil Magno
209              
210             This library is free software; you can redistribute it and/or modify
211             it under the same terms as Perl itself, either Perl version 5.20.1 or,
212             at your option, any later version of Perl 5 you may have available.
213              
214             =cut