File Coverage

blib/lib/Lingua/SU/Nums2Words.pm
Criterion Covered Total %
statement 70 70 100.0
branch 28 32 87.5
condition 47 63 74.6
subroutine 13 13 100.0
pod 2 2 100.0
total 160 180 88.8


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