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 1     1   143512 use 5.16.0;
  1         5  
7 1     1   7 use utf8;
  1         2  
  1         18  
8 1     1   35 use warnings;
  1         2  
  1         85  
9              
10             # {{{ use block
11              
12 1     1   761 use Export::Attrs;
  1         19300  
  1         7  
13              
14             # }}}
15             # {{{ variables declaration
16             our $VERSION = '0.2603250';
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 247211 my @a = @_;
52 15         52 return join_it(n2w1(@a));
53 1     1   263 }
  1         1  
  1         7  
54              
55             # }}}
56             # {{{ nums2words_simple
57              
58             sub nums2words_simple :Export {
59 6     6 1 5537 my @a = @_;
60 6         20 return join_it(n2w5(@a));
61 1     1   318 }
  1         2  
  1         5  
62              
63             # }}}
64              
65             ### private subs
66              
67             # for debugging
68             our $DEBUG = 0;
69 13 50   13 1 29 sub hmm___ { my @a = @_;print "(", (caller 1)[3], ") Hmm, ", @a if $DEBUG; return; }
  13         30  
  13         23  
70             # {{{ n2w1 handle scientific notation
71             sub n2w1 {
72 15   100 15 1 62 my $num = shift // return '';
73              
74 14 100       46 return $Zero_word if $num >= 10 ** 15; # not quadrillion and more
75              
76 13         17 my @words;
77              
78 13 50 33     101 $num =~ /^(.+)[Ee](.+)$/ and
79             @words = (n2w2($1), $Exp_word, n2w2($2)) or
80             @words = n2w2($num);
81              
82 13         71 return @words;
83             }
84              
85             # }}}
86             # {{{ n2w2 handle negative sign and decimal
87              
88             sub n2w2 {
89 13   50 13 1 38 my $num = shift // return '';
90 13         19 my $is_neg;
91 13         21 my @words = ();
92              
93             # negative
94 13 100       35 $num < 0 and $is_neg++;
95 13         88 $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     231 @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       32 $is_neg and
108             unshift @words, $Neg_word;
109              
110 13         47 return @words;
111             }
112              
113             # }}}
114             # {{{ n2w3 handle digits before decimal
115              
116             sub n2w3 {
117 13   50 13 1 45 my $num = shift // return '';
118 13         23 my @words = ();
119 13         19 my $order = 0;
120 13         22 my $t;
121              
122 13         92 while($num =~ /^(.*?)([\d\D*]{1,3})$/) {
123 25         51 $num = $1;
124 25         75 ($t = $2) =~ s/\D//g;
125 25   100     63 $t = $t || 0;
126 25 100       96 unshift @words, $Mult_words{$order} if $t > 0;
127 25         54 unshift @words, n2w4($t, $order);
128 25         152 $order++;
129             }
130              
131 13 100       73 @words = ($Zero_word) if not join('',@words)=~/\S/;
132 13         68 hmm___ "for the left part of decimal i get: @words\n";
133 13         60 return @words;
134             }
135              
136             # }}}
137             # {{{ n2w4 handle clusters of thousands
138              
139             sub n2w4 {
140 25   50 25 1 60 my $num = shift // return '';
141 25         37 my $order = shift;
142 25         43 my @words = ();
143              
144 25         45 my $n1 = $num % 10;
145 25         85 my $n2 = ($num % 100 - $n1) / 10;
146 25         52 my $n3 = ($num - $n2*10 - $n1) / 100;
147              
148             ($n3 == 0 && $n2 == 0 && $n1 > 0) && (((
149             $n1 == 1 && $order == 1) && (@words = ("se"))) ||
150 25 100 100     144 (@words = ($Digit_words{$n1}) ));
      66        
      100        
      100        
      100        
151              
152             $n3 == 1 and @words = ("seratus") or
153 25 100 100     100 $n3 > 1 and @words = ($Digit_words{$n3}, "ratus");
      66        
154              
155             $n2 == 1 and (
156             $n1 == 0 and push(@words, "sepuluh") or
157             $n1 == 1 and push(@words, "sebelas") or
158 25 100 33     69 push(@words, $Digit_words{$n1}, "belas")
      0        
      33        
      0        
159             );
160              
161 25 100       56 $n2 > 1 and do {
162 7         164 push @words, $Digit_words{$n2}, "puluh";
163 7 100       26 push @words, $Digit_words{$n1} if $n1 > 0;
164             };
165              
166             ($n3 > 0 && $n2 == 0 && $n1 > 0) &&
167 25 50 100     78 push @words, $Digit_words{$n1} ;
      66        
168              
169 25 100 100     158 ($n3 != 0 || $n2 != 0 || $n1 != 0) &&
      100        
170             return @words;
171             }
172              
173             # }}}
174             # {{{ n2w5 handle digits after decimal
175             sub n2w5 {
176 6   100 6 1 24 my $num = shift // return '';
177              
178 5 100       18 return $Zero_word if $num >= 10 ** 15; # not quadrillion and more
179              
180 4         16 my @words = ();
181 4         8 my $i;
182             my $t;
183              
184 4         19 for( $i=0 ; $i<=length($num)-1 ; $i++ ) {
185 22         43 $t = substr($num, $i, 1);
186             exists $Digit_words{$t} and
187 22 50       84 push @words, $Digit_words{$t};
188             }
189              
190 4 50       28 @words = ($Zero_word) if not join('',@words)=~/\S/;
191 4         20 return @words;
192             }
193              
194             # }}}
195             # {{{ join_it join array of words, also join (se, ratus) -> seratus, etc.
196             sub join_it {
197 21     21 1 63 my @a = @_;
198 21         40 my $words = '';
199 21         29 my $w;
200              
201 21         61 while(defined( $w = shift @a)) {
202 95         188 $words .= $w;
203 95 100 100     539 $words .= ' ' unless not length $w or $w eq 'se' or not @a;
      100        
204             }
205 21         103 return $words;
206             }
207              
208             # }}}
209              
210             1;
211             __END__