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