File Coverage

blib/lib/Lingua/IND/Nums2Words.pm
Criterion Covered Total %
statement 81 81 100.0
branch 32 38 84.2
condition 50 75 66.6
subroutine 15 15 100.0
pod 9 9 100.0
total 187 218 85.7


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1) -*-
2              
3             package Lingua::IND::Nums2Words;
4             # ABSTRACT: Number 2 word conversion in IND.
5              
6             # {{{ use block
7              
8 1     1   109773 use 5.10.1;
  1         3  
9 1     1   4 use strict;
  1         2  
  1         27  
10 1     1   3 use warnings;
  1         15  
  1         56  
11              
12 1     1   581 use Export::Attrs;
  1         12482  
  1         7  
13              
14             # }}}
15             # {{{ variables declaration
16             our $VERSION = '0.2603230';
17              
18             our $Dec_char = ".";
19             our $Neg_word = "negatif";
20             our $Dec_word = "koma";
21             our $Exp_word = "dikali sepuluh pangkat";
22             our $Zero_word = "nol";
23              
24             our %Digit_words = (
25             0 => $Zero_word,
26             1 => 'satu',
27             2 => 'dua',
28             3 => 'tiga',
29             4 => 'empat',
30             5 => 'lima',
31             6 => 'enam',
32             7 => 'tujuh',
33             8 => 'delapan',
34             9 => 'sembilan'
35             );
36              
37             our %Mult_words = (
38             0 => '',
39             1 => 'ribu',
40             2 => 'juta',
41             3 => 'milyar',
42             4 => 'triliun'
43             );
44              
45             # }}}
46              
47             ### public subs
48             # {{{ nums2words
49              
50             sub nums2words :Export {
51 15     15 1 262000 my @a = @_;
52 15         53 return join_it(n2w1(@a));
53 1     1   344 }
  1         3  
  1         17  
54              
55             # }}}
56             # {{{ nums2words_simple
57              
58             sub nums2words_simple :Export {
59 6     6 1 5698 my @a = @_;
60 6         22 return join_it(n2w5(@a));
61 1     1   361 }
  1         2  
  1         5  
62              
63             # }}}
64              
65             ### private subs
66              
67             # for debugging
68             our $DEBUG = 0;
69 13 50   13 1 28 sub hmm___ { my @a = @_;print "(", (caller 1)[3], ") Hmm, ", @a if $DEBUG; return; }
  13         32  
  13         25  
70             # {{{ n2w1 handle scientific notation
71             sub n2w1 {
72 15   100 15 1 56 my $num = shift // return '';
73              
74 14 100       43 return $Zero_word if $num >= 10 ** 15; # not quadrillion and more
75              
76 13         25 my @words;
77              
78 13 50 33     102 $num =~ /^(.+)[Ee](.+)$/ and
79             @words = (n2w2($1), $Exp_word, n2w2($2)) or
80             @words = n2w2($num);
81              
82 13         52 return @words;
83             }
84              
85             # }}}
86             # {{{ n2w2 handle negative sign and decimal
87              
88             sub n2w2 {
89 13   50 13 1 40 my $num = shift // return '';
90 13         21 my $is_neg;
91 13         24 my @words = ();
92              
93             # negative
94 13 100       39 $num < 0 and $is_neg++;
95 13         87 $num =~ s/^[\s\t]*[+-]*(.*)/$1/;
96              
97             # decimal
98             $num =~ /^(.+)\Q$Dec_char\E(.+)$/o and
99             @words = (n2w3($1), $Dec_word, n2w5($2)) or
100              
101             $num =~ /^\Q$Dec_char\E(.+)$/o and
102 13 50 50     222 @words = ($Digit_words{0}, $Dec_word, n2w5($1)) or
      33        
      33        
      33        
103              
104             $num =~ /^(.+)(?:\Q$Dec_char\E)?$/o and
105             @words = n2w3($1);
106              
107 13 100       39 $is_neg and
108             unshift @words, $Neg_word;
109              
110 13         47 return @words;
111             }
112              
113              
114             # }}}
115             # {{{ n2w3 handle digits before decimal
116              
117             sub n2w3 {
118 13   50 13 1 45 my $num = shift // return '';
119 13         24 my @words = ();
120 13         49 my $order = 0;
121 13         23 my $t;
122              
123 13         66 while($num =~ /^(.*?)([\d\D*]{1,3})$/) {
124 25         58 $num = $1;
125 25         76 ($t = $2) =~ s/\D//g;
126 25   100     67 $t = $t || 0;
127 25 100       102 unshift @words, $Mult_words{$order} if $t > 0;
128 25         58 unshift @words, n2w4($t, $order);
129 25         130 $order++;
130             }
131              
132 13 100       75 @words = ($Zero_word) if not join('',@words)=~/\S/;
133 13         70 hmm___ "for the left part of decimal i get: @words\n";
134 13         58 return @words;
135             }
136              
137             # }}}
138             # {{{ n2w4 handle clusters of thousands
139              
140             sub n2w4 {
141 25   50 25 1 60 my $num = shift // return '';
142 25         78 my $order = shift;
143 25         46 my @words = ();
144              
145 25         51 my $n1 = $num % 10;
146 25         93 my $n2 = ($num % 100 - $n1) / 10;
147 25         56 my $n3 = ($num - $n2*10 - $n1) / 100;
148              
149             ($n3 == 0 && $n2 == 0 && $n1 > 0) && (((
150             $n1 == 1 && $order == 1) && (@words = ("se"))) ||
151 25 100 100     151 (@words = ($Digit_words{$n1}) ));
      66        
      100        
      100        
      100        
152              
153             $n3 == 1 and @words = ("seratus") or
154 25 100 100     117 $n3 > 1 and @words = ($Digit_words{$n3}, "ratus");
      66        
155              
156             $n2 == 1 and (
157             $n1 == 0 and push(@words, "sepuluh") or
158             $n1 == 1 and push(@words, "sebelas") or
159 25 100 33     78 push(@words, $Digit_words{$n1}, "belas")
      0        
      33        
      0        
160             );
161              
162 25 100       63 $n2 > 1 and do {
163 7         23 push @words, $Digit_words{$n2}, "puluh";
164 7 100       23 push @words, $Digit_words{$n1} if $n1 > 0;
165             };
166              
167             ($n3 > 0 && $n2 == 0 && $n1 > 0) &&
168 25 50 100     94 push @words, $Digit_words{$n1} ;
      66        
169              
170 25 100 100     165 ($n3 != 0 || $n2 != 0 || $n1 != 0) &&
      100        
171             return @words;
172             }
173              
174             # }}}
175             # {{{ n2w5 handle digits after decimal
176             sub n2w5 {
177 6   100 6 1 22 my $num = shift // return '';
178              
179 5 100       16 return $Zero_word if $num >= 10 ** 15; # not quadrillion and more
180              
181 4         17 my @words = ();
182 4         7 my $i;
183             my $t;
184              
185 4         22 for( $i=0 ; $i<=length($num)-1 ; $i++ ) {
186 22         42 $t = substr($num, $i, 1);
187             exists $Digit_words{$t} and
188 22 50       84 push @words, $Digit_words{$t};
189             }
190              
191 4 50       28 @words = ($Zero_word) if not join('',@words)=~/\S/;
192 4         20 return @words;
193             }
194              
195             # }}}
196             # {{{ join_it join array of words, also join (se, ratus) -> seratus, etc.
197             sub join_it {
198 21     21 1 65 my @a = @_;
199 21         40 my $words = '';
200 21         31 my $w;
201              
202 21         84 while(defined( $w = shift @a)) {
203 95         175 $words .= $w;
204 95 100 100     511 $words .= ' ' unless not length $w or $w eq 'se' or not @a;
      100        
205             }
206 21         83 return $words;
207             }
208              
209             # }}}
210              
211             1;
212             __END__