File Coverage

blib/lib/Lingua/SU/Nums2Words.pm
Criterion Covered Total %
statement 71 71 100.0
branch 28 32 87.5
condition 47 63 74.6
subroutine 13 13 100.0
pod 0 2 0.0
total 159 181 87.8


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