line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package Lingua::EN::Numbers; |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
require Exporter; |
5
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
6
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
33139
|
use 5.006; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
146
|
|
8
|
4
|
|
|
4
|
|
18
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
134
|
|
9
|
4
|
|
|
4
|
|
17
|
use warnings; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
252
|
|
10
|
4
|
50
|
|
4
|
|
112
|
BEGIN { *DEBUG = sub () {0} unless defined &DEBUG } # setup a DEBUG constant |
11
|
4
|
|
|
|
|
5734
|
use vars qw( |
12
|
|
|
|
|
|
|
@EXPORT @EXPORT_OK $VERSION |
13
|
|
|
|
|
|
|
%D %Card2ord %Mult |
14
|
4
|
|
|
4
|
|
19
|
); |
|
4
|
|
|
|
|
7
|
|
15
|
|
|
|
|
|
|
$VERSION = '2.02'; |
16
|
|
|
|
|
|
|
@EXPORT = (); |
17
|
|
|
|
|
|
|
@EXPORT_OK = qw( num2en num2en_ordinal ); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
@D{0 .. 20, 30,40,50,60,70,80,90} = qw| |
20
|
|
|
|
|
|
|
zero |
21
|
|
|
|
|
|
|
one two three four five six seven eight nine ten |
22
|
|
|
|
|
|
|
eleven twelve thirteen fourteen fifteen |
23
|
|
|
|
|
|
|
sixteen seventeen eighteen nineteen |
24
|
|
|
|
|
|
|
twenty thirty forty fifty sixty seventy eighty ninety |
25
|
|
|
|
|
|
|
|; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
@Card2ord{ qw| one two three five eight nine twelve |} |
29
|
|
|
|
|
|
|
= qw| first second third fifth eighth ninth twelfth |; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
{ |
33
|
|
|
|
|
|
|
my $c = 0; |
34
|
|
|
|
|
|
|
for ( '', qw< |
35
|
|
|
|
|
|
|
thousand million billion trillion quadrillion quintillion sextillion |
36
|
|
|
|
|
|
|
septillion octillion nonillion |
37
|
|
|
|
|
|
|
> ) { |
38
|
|
|
|
|
|
|
$Mult{$c} = $_; |
39
|
|
|
|
|
|
|
$c++; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
#========================================================================== |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub num2en_ordinal { |
46
|
|
|
|
|
|
|
# Cardinals are [one two three...] |
47
|
|
|
|
|
|
|
# Ordinals are [first second third...] |
48
|
|
|
|
|
|
|
|
49
|
6
|
50
|
33
|
6
|
0
|
25
|
return undef unless defined $_[0] and length $_[0]; |
50
|
6
|
|
|
|
|
8
|
my($x) = $_[0]; |
51
|
|
|
|
|
|
|
|
52
|
6
|
|
|
|
|
15
|
$x = num2en($x); |
53
|
6
|
50
|
|
|
|
13
|
return $x unless $x; |
54
|
6
|
50
|
|
|
|
29
|
$x =~ s/(\w+)$//s or return $x . "th"; |
55
|
6
|
|
|
|
|
11
|
my $last = $1; |
56
|
|
|
|
|
|
|
|
57
|
6
|
|
33
|
|
|
30
|
$last = |
58
|
|
|
|
|
|
|
$Card2ord{$last} || ( $last =~ s/y$/ieth/ && $last ) || ( $last !~ /th$/ ? $last . "th" : $last ); |
59
|
|
|
|
|
|
|
|
60
|
6
|
|
|
|
|
16
|
return "$x$last"; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
#========================================================================== |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub num2en { |
66
|
86
|
|
|
86
|
0
|
591
|
my $x = $_[0]; |
67
|
86
|
100
|
100
|
|
|
377
|
return undef unless defined $x and length $x; |
68
|
|
|
|
|
|
|
|
69
|
84
|
50
|
|
|
|
133
|
return 'not-a-number' if $x eq 'NaN'; |
70
|
84
|
50
|
|
|
|
135
|
return 'positive infinity' if $x =~ m/^\+inf(?:inity)?$/si; |
71
|
84
|
50
|
|
|
|
115
|
return 'negative infinity' if $x =~ m/^\-inf(?:inity)?$/si; |
72
|
84
|
50
|
|
|
|
129
|
return 'infinity' if $x =~ m/^inf(?:inity)?$/si; |
73
|
|
|
|
|
|
|
|
74
|
84
|
100
|
|
|
|
213
|
return $D{$x} if exists $D{$x}; # the most common cases |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# Make sure it's not in scientific notation: |
77
|
54
|
100
|
|
|
|
52
|
{ my $e = _e2en($x); return $e if defined $e; } |
|
54
|
|
|
|
|
78
|
|
|
54
|
|
|
|
|
108
|
|
78
|
|
|
|
|
|
|
|
79
|
49
|
|
|
|
|
46
|
my $orig = $x; |
80
|
|
|
|
|
|
|
|
81
|
49
|
|
|
|
|
64
|
$x =~ s/,//g; # nix any commas |
82
|
|
|
|
|
|
|
|
83
|
49
|
|
|
|
|
35
|
my $sign; |
84
|
49
|
100
|
|
|
|
128
|
$sign = $1 if $x =~ s/^([-+])//s; |
85
|
|
|
|
|
|
|
|
86
|
49
|
|
|
|
|
43
|
my($int, $fract); |
87
|
49
|
100
|
|
|
|
178
|
if( $x =~ m<^\d+$> ) { $int = $x } |
|
33
|
100
|
|
|
|
36
|
|
|
|
100
|
|
|
|
|
|
88
|
9
|
|
|
|
|
16
|
elsif( $x =~ m<^(\d+)\.(\d+)$> ) { $int = $1; $fract = $2 } |
|
9
|
|
|
|
|
11
|
|
89
|
3
|
|
|
|
|
4
|
elsif( $x =~ m<^\.(\d+)$> ) { $fract = $1 } |
90
|
|
|
|
|
|
|
else { |
91
|
4
|
|
|
|
|
3
|
DEBUG and print "Not a number: \"orig\"\n"; |
92
|
4
|
|
|
|
|
13
|
return undef; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
45
|
|
|
|
|
36
|
DEBUG and printf " Working on Sign[%s] Int2en[%s] Fract[%s] < \"%s\"\n", |
96
|
|
|
|
|
|
|
map defined($_) ? $_ : "nil", $sign, $int, $fract, $orig; |
97
|
|
|
|
|
|
|
|
98
|
45
|
|
66
|
|
|
61
|
return join ' ', grep defined($_) && length($_), |
99
|
|
|
|
|
|
|
_sign2en($sign), |
100
|
|
|
|
|
|
|
_int2en($int), |
101
|
|
|
|
|
|
|
_fract2en($fract), |
102
|
|
|
|
|
|
|
; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub _sign2en { |
108
|
45
|
100
|
66
|
45
|
|
139
|
return undef unless defined $_[0] and length $_[0]; |
109
|
13
|
100
|
|
|
|
40
|
return 'negative' if $_[0] eq '-'; |
110
|
4
|
50
|
|
|
|
11
|
return 'positive' if $_[0] eq '+'; |
111
|
0
|
|
|
|
|
0
|
return "WHAT_IS_$_[0]"; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub _fract2en { # "1234" => "point one two three four" |
115
|
45
|
100
|
66
|
45
|
|
401
|
return undef unless defined $_[0] and length $_[0]; |
116
|
12
|
|
|
|
|
12
|
my $x = $_[0]; |
117
|
12
|
|
|
|
|
143
|
return join ' ', 'point', map $D{$_}, split '', $x; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
121
|
|
|
|
|
|
|
# The real work: |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub _int2en { |
124
|
74
|
50
|
66
|
74
|
|
451
|
return undef unless defined $_[0] and length $_[0] |
|
|
|
66
|
|
|
|
|
125
|
|
|
|
|
|
|
and $_[0] =~ m/^\d+$/s; |
126
|
|
|
|
|
|
|
|
127
|
71
|
|
|
|
|
77
|
my($x) = $_[0]; |
128
|
|
|
|
|
|
|
|
129
|
71
|
100
|
|
|
|
174
|
return $D{$x} if defined $D{$x}; # most common/irreg cases |
130
|
|
|
|
|
|
|
|
131
|
43
|
100
|
|
|
|
127
|
if( $x =~ m/^(.)(.)$/ ) { |
|
|
100
|
|
|
|
|
|
132
|
23
|
|
|
|
|
106
|
return $D{$1 . '0'} . '-' . $D{$2}; |
133
|
|
|
|
|
|
|
# like forty - two |
134
|
|
|
|
|
|
|
# note that neither bit can be zero at this point |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
} elsif( $x =~ m/^(.)(..)$/ ) { |
137
|
11
|
|
|
|
|
39
|
my($h, $rest) = ("$D{$1} hundred", $2); |
138
|
11
|
100
|
|
|
|
34
|
return $h if $rest eq '00'; |
139
|
10
|
|
|
|
|
36
|
return "$h and " . _int2en(0 + $rest); |
140
|
|
|
|
|
|
|
} else { |
141
|
9
|
|
|
|
|
18
|
return _bigint2en($x); |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub _bigint2en { |
148
|
9
|
50
|
33
|
9
|
|
78
|
return undef unless defined $_[0] and length $_[0] |
|
|
|
33
|
|
|
|
|
149
|
|
|
|
|
|
|
and $_[0] =~ m/^\d+$/s; |
150
|
|
|
|
|
|
|
|
151
|
9
|
|
|
|
|
505
|
my($x) = $_[0]; |
152
|
|
|
|
|
|
|
|
153
|
9
|
|
|
|
|
26
|
my @chunks; # each: [ string, exponent ] |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
{ |
156
|
9
|
|
|
|
|
9
|
my $groupnum = 0; |
|
9
|
|
|
|
|
10
|
|
157
|
9
|
|
|
|
|
8
|
my $num; |
158
|
9
|
|
|
|
|
40
|
while( $x =~ s<(\d{1,3})$><>s ) { # pull at most three digits from the end |
159
|
23
|
|
|
|
|
38
|
$num = $1 + 0; |
160
|
23
|
100
|
|
|
|
48
|
unshift @chunks, [ $num, $groupnum ] if $num; |
161
|
23
|
|
|
|
|
61
|
++$groupnum; |
162
|
|
|
|
|
|
|
} |
163
|
9
|
50
|
|
|
|
19
|
return $D{'0'} unless @chunks; # rare but possible |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
9
|
|
|
|
|
8
|
my $and; |
167
|
|
|
|
|
|
|
|
168
|
9
|
100
|
100
|
|
|
40
|
$and = 'and' if $chunks[-1][1] == 0 and $chunks[-1][0] < 100; |
169
|
|
|
|
|
|
|
# The special 'and' that shows up in like "one thousand and eight" |
170
|
|
|
|
|
|
|
# and "two billion and fifteen", but not "one thousand [*and] five hundred" |
171
|
|
|
|
|
|
|
# or "one million, [*and] nine" |
172
|
|
|
|
|
|
|
|
173
|
9
|
|
|
|
|
35
|
_chunks2en( \@chunks ); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# bugfix: neilb: deals with case where we have at least millions, thousands, |
176
|
|
|
|
|
|
|
# but 00N for the last chunk. |
177
|
|
|
|
|
|
|
# $chunks[-2] .= " and" if $and and @chunks > 1; |
178
|
9
|
100
|
66
|
|
|
32
|
if ($and and @chunks > 1) { |
179
|
6
|
|
|
|
|
15
|
$chunks[-2] .= " and $chunks[-1]"; |
180
|
6
|
|
|
|
|
7
|
pop(@chunks); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
9
|
100
|
100
|
|
|
25
|
return "$chunks[0] $chunks[1]" if @chunks == 2 and !$and; |
184
|
|
|
|
|
|
|
# Avoid having a comma if just two units |
185
|
8
|
|
|
|
|
26
|
return join ", ", @chunks; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub _chunks2en { |
190
|
9
|
|
|
9
|
|
17
|
my $chunks = $_[0]; |
191
|
9
|
50
|
|
|
|
19
|
return unless @$chunks; |
192
|
9
|
|
|
|
|
8
|
my @out; |
193
|
9
|
|
|
|
|
16
|
foreach my $c (@$chunks) { |
194
|
19
|
50
|
|
|
|
49
|
push @out, $c = _groupify( _int2en( $c->[0] ), $c->[1] ) if $c->[0]; |
195
|
|
|
|
|
|
|
} |
196
|
9
|
|
|
|
|
20
|
@$chunks = @out; |
197
|
9
|
|
|
|
|
9
|
return; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub _groupify { |
201
|
|
|
|
|
|
|
# turn ("seventeen", 3) => "seventeen billion" |
202
|
19
|
|
|
19
|
|
17
|
my($basic, $multnum) = @_; |
203
|
19
|
100
|
|
|
|
40
|
return $basic unless $multnum; # the first group is unitless |
204
|
12
|
|
|
|
|
10
|
DEBUG > 2 and print " Groupifying $basic x $multnum mults\n"; |
205
|
12
|
50
|
|
|
|
60
|
return "$basic $Mult{$multnum}" if $Mult{$multnum}; |
206
|
|
|
|
|
|
|
# Otherwise it must be huuuuuge, so fake it with scientific notation |
207
|
0
|
|
|
|
|
0
|
return "$basic " . "times ten to the " . num2en_ordinal($multnum * 3); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
211
|
|
|
|
|
|
|
# |
212
|
|
|
|
|
|
|
# Because I can never remember this: |
213
|
|
|
|
|
|
|
# |
214
|
|
|
|
|
|
|
# 3.1E8 |
215
|
|
|
|
|
|
|
# ^^^ is called the "mantissa" |
216
|
|
|
|
|
|
|
# ^ is called the "exponent" |
217
|
|
|
|
|
|
|
# (the implicit "10" is the "base" a/k/a "radix") |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub _e2en { |
220
|
54
|
|
|
54
|
|
62
|
my $x = $_[0]; |
221
|
|
|
|
|
|
|
|
222
|
54
|
|
|
|
|
57
|
my($m, $e); |
223
|
54
|
100
|
|
|
|
191
|
if( $x =~ |
224
|
|
|
|
|
|
|
m< |
225
|
|
|
|
|
|
|
^( |
226
|
|
|
|
|
|
|
[-+]? # leading sign |
227
|
|
|
|
|
|
|
(?: |
228
|
|
|
|
|
|
|
[\d,]+ | [\d,]*\.\d+ # number |
229
|
|
|
|
|
|
|
) |
230
|
|
|
|
|
|
|
) |
231
|
|
|
|
|
|
|
[eE] |
232
|
|
|
|
|
|
|
([-+]?\d+) # mantissa, has to be an integer |
233
|
|
|
|
|
|
|
$ |
234
|
|
|
|
|
|
|
>x |
235
|
|
|
|
|
|
|
) { |
236
|
5
|
|
|
|
|
10
|
($m, $e) = ($1, $2); |
237
|
5
|
|
|
|
|
3
|
DEBUG and print " Scientific notation: [$x] => $m E $e\n"; |
238
|
5
|
|
|
|
|
8
|
$e += 0; |
239
|
5
|
|
|
|
|
9
|
return num2en($m) . ' times ten to the ' . num2en_ordinal($e); |
240
|
|
|
|
|
|
|
} else { |
241
|
49
|
|
|
|
|
30
|
DEBUG and print " Okay, $x isn't in exponential notation\n"; |
242
|
49
|
|
|
|
|
73
|
return undef; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
#========================================================================== |
247
|
|
|
|
|
|
|
1; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
__END__ |