File Coverage

blib/lib/Lingua/ID/Nums2Words.pm
Criterion Covered Total %
statement 68 68 100.0
branch 28 32 87.5
condition 51 62 82.2
subroutine 12 12 100.0
pod 0 2 0.0
total 159 176 90.3


line stmt bran cond sub pod time code
1             package Lingua::ID::Nums2Words;
2              
3 1     1   51239 use 5.010;
  1         4  
  1         44  
4 1     1   6 use strict;
  1         2  
  1         39  
5 1     1   6 use warnings;
  1         1  
  1         152  
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT_OK = qw(nums2words nums2words_simple);
10              
11             our %SPEC;
12              
13             our $VERSION = '0.03'; # VERSION
14              
15 1         1585 use vars qw(
16             $Dec_char
17             $Neg_word
18             $Dec_word
19             $Exp_word
20             $Zero_word
21             %Digit_words
22             %Mult_words
23 1     1   7 );
  1         3  
24              
25             $Dec_char = ".";
26             $Neg_word = "negatif";
27             $Dec_word = "koma";
28             $Exp_word = "dikali sepuluh pangkat";
29             $Zero_word = "nol";
30              
31             %Digit_words = (
32             0 => $Zero_word,
33             1 => 'satu',
34             2 => 'dua',
35             3 => 'tiga',
36             4 => 'empat',
37             5 => 'lima',
38             6 => 'enam',
39             7 => 'tujuh',
40             8 => 'delapan',
41             9 => 'sembilan'
42             );
43              
44             %Mult_words = (
45             0 => '',
46             1 => 'ribu',
47             2 => 'juta',
48             3 => 'milyar',
49             4 => 'triliun'
50             );
51              
52             $SPEC{nums2words} = {
53             v => 1.1,
54             summary => 'Convert number to Indonesian verbage',
55             description => <<'_',
56              
57             This is akin to converting 123 to "a hundred and twenty three" in English.
58             Currently can handle real numbers in normal and scientific form in the order of
59             hundreds of trillions. It also preserves formatting in the number string (e.g,
60             given "1.00" `nums2words` will pronounce the zeros.
61              
62             _
63             args => {
64             num => {
65             schema => 'str*',
66             summary => 'The number to convert',
67             req => 1,
68             pos => 0,
69             },
70             },
71             args_as => 'array',
72             result_naked => 1,
73             };
74 29     29 0 11910 sub nums2words($) { _join_it(_handle_scinotation(@_)) }
75              
76             $SPEC{nums2words_simple} = {
77             v => 1.1,
78             summary => 'Like nums2words but only pronounce the digits',
79             description => <<'_',
80              
81             This is akin to converting 123 to "one two three" in English.
82              
83             _
84             args => {
85             num => {
86             schema => 'str*',
87             summary => 'The number to convert',
88             req => 1,
89             pos => 0,
90             },
91             },
92             args_as => 'array',
93             result_naked => 1,
94             };
95 5     5 0 5021 sub nums2words_simple($) { _join_it(_handle_dec(@_)) }
96              
97             sub _handle_scinotation($) {
98 29     29   47 my $num = shift;
99 29         32 my @words;
100              
101 29 100 66     149 $num =~ /^(.+)[Ee](.+)$/ and
102             @words = (_handle_neg_dec($1), $Exp_word, _handle_neg_dec($2)) or
103             @words = _handle_neg_dec($num);
104              
105 29         104 @words;
106             }
107              
108             sub _handle_neg_dec($) {
109 33     33   43 my $num = shift;
110 33         33 my $is_neg;
111 33         43 my @words = ();
112              
113 33 100       71 $num < 0 and $is_neg++;
114 33         134 $num =~ s/^[\s\t]*[+-]*(.*)/$1/;
115              
116 33 50 50     307 $num =~ /^(.+)\Q$Dec_char\E(.+)$/o and
      66        
      33        
      66        
117             @words = (_handle_int($1), $Dec_word, _handle_dec($2)) or
118              
119             $num =~ /^\Q$Dec_char\E(.+)$/o and
120             @words = ($Digit_words{0}, $Dec_word, _handle_dec($1)) or
121              
122             $num =~ /^(.+)(?:\Q$Dec_char\E)?$/o and
123             @words = _handle_int($1);
124              
125 33 100       78 $is_neg and
126             unshift @words, $Neg_word;
127              
128 33         123 @words;
129             }
130              
131             # handle digits before decimal
132             sub _handle_int($) {
133 33     33   58 my $num = shift;
134 33         50 my @words = ();
135 33         37 my $order = 0;
136 33         28 my $t;
137              
138 33         132 while($num =~ /^(.*?)([\d\D*]{1,3})$/) {
139 57         105 $num = $1;
140 57         102 ($t = $2) =~ s/\D//g;
141 57 100       350 unshift @words, $Mult_words{$order} if $t > 0;
142 57         98 unshift @words, _handle_thousand($t, $order);
143 57         1258 $order++;
144             }
145              
146 33 100       127 @words = ($Zero_word) if not join('',@words)=~/\S/;
147 33         117 @words;
148             }
149              
150             sub _handle_thousand($$) {
151 57     57   73 my $num = shift;
152 57         60 my $order = shift;
153 57         78 my @words = ();
154              
155 57         72 my $n1 = $num % 10;
156 57         86 my $n2 = ($num % 100 - $n1) / 10;
157 57         77 my $n3 = ($num - $n2*10 - $n1) / 100;
158              
159 57 100 100     919 $n3 == 0 && $n2 == 0 && $n1 > 0 and (
      66        
      100        
      100        
      100        
160             $n1 == 1 && $order == 1 and @words = ("se") or
161             @words = ($Digit_words{$n1}) );
162              
163 57 100 100     181 $n3 == 1 and @words = ("seratus") or
      66        
164             $n3 > 1 and @words = ($Digit_words{$n3}, "ratus");
165              
166 57 100 66     306 $n2 == 1 and (
      66        
      66        
      100        
167             $n1 == 0 and push(@words, "sepuluh") or
168             $n1 == 1 and push(@words, "sebelas") or
169             push(@words, $Digit_words{$n1}, "belas")
170             );
171              
172 57 100       99 $n2 > 1 and do {
173 7         15 push @words, $Digit_words{$n2}, "puluh";
174 7 50       21 push @words, $Digit_words{$n1} if $n1 > 0;
175             };
176              
177 57 100 100     144 $n3 > 0 && $n2 == 0 && $n1 > 0 and
      100        
178             push @words, $Digit_words{$n1};
179              
180 57 100 100     1102 $n3 != 0 || $n2 != 0 || $n1 != 0 and
      100        
181             @words;
182             }
183              
184             # handle digits after decimal
185             sub _handle_dec($) {
186 13     13   58 my $num = shift;
187 13         20 my @words = ();
188 13         20 my $i;
189             my $t;
190              
191 13         38 for( $i=0; $i<=length($num)-1; $i++ ) {
192 30         41 $t = substr($num, $i, 1);
193 30 50       119 exists $Digit_words{$t} and
194             push @words, $Digit_words{$t};
195             }
196              
197 13 50       52 @words = ($Zero_word) if not join('',@words)=~/\S/;
198 13         60 @words;
199             }
200              
201             # join array of words, also join (se, ratus) -> seratus, etc.
202             sub _join_it(@) {
203 34     34   41 my $words = '';
204 34         35 my $w;
205              
206 34         75 while(defined( $w = shift )) {
207 174         191 $words .= $w;
208 174 100 100     996 $words .= ' ' unless not length $w or $w eq 'se' or not @_;
      100        
209             }
210 34         63 $words =~ s/^\s+//;
211 34         113 $words =~ s/\s+$//;
212 34         191 $words;
213             }
214              
215             1;
216             # ABSTRACT: Convert number to Indonesian verbage
217              
218              
219             __END__