File Coverage

blib/lib/Math/BigInt/Named/English.pm
Criterion Covered Total %
statement 74 74 100.0
branch 30 32 93.7
condition 11 12 91.6
subroutine 6 6 100.0
pod 1 1 100.0
total 122 125 97.6


line stmt bran cond sub pod time code
1             # -*- mode: perl; -*-
2              
3             package Math::BigInt::Named::English;
4              
5 3     3   85630 use strict;
  3         15  
  3         87  
6 3     3   15 use warnings;
  3         4  
  3         80  
7              
8 3     3   377 use Math::BigInt::Named;
  3         8  
  3         30  
9             our @ISA = qw< Math::BigInt::Named >;
10              
11             our $VERSION = '0.08';
12              
13             my $SMALL = [ qw/
14             zero
15             one
16             two
17             three
18             four
19             five
20             six
21             seven
22             eight
23             nine
24             ten
25             eleven
26             twelve
27             thirteen
28             fourteen
29             fifteen
30             sixteen
31             seventeen
32             eighteen
33             nineteen
34             / ];
35              
36             my $TENS = [ qw /
37             ten
38             twenty
39             thirty
40             fourty
41             fifty
42             sixty
43             seventy
44             eighty
45             ninety
46             / ];
47              
48             my $HUNDREDS = [ qw /
49             one
50             two
51             three
52             four
53             five
54             six
55             seven
56             eight
57             nine
58             / ];
59              
60             my $TRIPLE = [ qw /
61             mi
62             bi
63             tri
64             quadri
65             penti
66             hexi
67             septi
68             octi
69             / ];
70              
71             sub name {
72 56     56 1 98600 my $x = shift;
73 56 50       167 $x = Math::BigInt -> new($x) unless ref($x);
74              
75 56         87 my $class = ref($x);
76              
77 56 50       125 return '' if $x -> is_nan();
78              
79 56         334 my $ret = '';
80 56         119 my $y = $x -> copy();
81 56         1016 my $rem;
82              
83 56 100       135 if ($y -> sign() eq '-') {
84 1         7 $ret = 'minus ';
85 1         7 $y -> babs();
86             }
87              
88 56 100       457 if ($y < 1000) {
89 45         4243 return $ret . $class -> _triple($y, 1, 0);
90             }
91              
92             # Split the number into numerical triplets.
93              
94 11         1581 my @num = ();
95 11         25 while (!$y -> is_zero()) {
96 25         276 ($y, $rem) = $y -> bdiv(1000);
97 25         5183 unshift @num, $rem;
98             }
99              
100             # Convert each numerical triplet into a string.
101              
102 11         227 my @str = ();
103 11         26 for my $i (0 .. $#num) {
104 25         34 my $num = $num[$i];
105 25         28 my $str;
106 25         40 my $index = $#num - $i;
107              
108 25         32 my $count;
109 25         49 $count = $class -> _triple($num, 0, $i);
110 25         2526 $str .= $count;
111              
112 25 100       54 if ($index > 0) {
113 14         32 my $triple_name = $class -> _triple_name($#num - $i, $num);
114 14         58 $str .= ' ' . $triple_name;
115             }
116              
117 25         50 $str[$i] = $str;
118             }
119              
120             # 1100 -> "one thousand one hundred" (not "one thousand and one hundred")
121             # 1099 -> "one thousand and ninety-nine" (not "one thousand ninety-nine")
122             # 1098 -> "one thousand and ninety-eight" (not "one thousand ninety-eight")
123             # ...
124             # 1001 -> "one thousand and one" (not "one thousand one")
125             # 1000 -> "one thousand" (not "one thousand and zero")
126              
127 11 100 66     45 if (@num > 1 && 0 < $num[-1] && $num[-1] < 100) {
      100        
128 5         1124 splice @str, -1, 0, "and";
129             }
130              
131 11         1298 $ret . join(" ", grep /\S/, @str);
132             }
133              
134             sub _triple_name {
135 49     49   1159 my ($self, $index, $number) = @_;
136             # index => 0 hundreds, tens and ones
137             # index => 1 thousands
138             # index => 2 millions
139              
140 49 100 100     186 return '' if $index == 0 || $number -> is_zero();
141 44 100       20949 return 'thousand' if $index == 1;
142              
143 35         66 my $postfix = 'llion';
144 35         42 my $plural = 's';
145 35 100       69 if (($index & 1) == 1) {
146 16         25 $postfix = 'lliard';
147             }
148 35 100       76 $postfix .= $plural unless $number -> is_one();
149 35         395 $index -= 2;
150 35         144 return $TRIPLE -> [$index >> 1] . $postfix;
151             }
152              
153             sub _triple {
154             # return name of a triple
155             # input: number >= 0, < 1000
156             # only true if triple is the only triple
157 70     70   132 my ($self, $number, $only) = @_;
158              
159             # 0 => null, but only if there is just one triple
160 70 100 100     139 return '' if $number -> is_zero() && !$only;
161              
162             # we have the full name for these
163 65 100       736 return $SMALL -> [$number] if $number <= $#$SMALL;
164              
165             # New code:
166              
167 30         2897 my @num = ();
168 30         81 $num[1] = $number % 100; # tens and ones
169 30         4652 $num[0] = ($number - $num[1]) / 100; # hundreds
170              
171 30         21940 my @str = ();
172              
173             # Do the hundreds, if any.
174              
175 30 100       85 if ($num[0]) {
176 18         354 my $str;
177 18         42 $str = $HUNDREDS -> [$num[0] - 1];
178 18         3136 $str .= " hundred";
179 18         39 push @str, $str;
180             }
181              
182             # Do the tens and ones, if any.
183              
184 30 100       317 if ($num[1]) {
185 19         349 my $str;
186 19         44 my $ones = $num[1] % 10;
187 19         3195 my $tens = ($num[1] - $ones) / 10;
188 19 100       6186 if ($num[1] <= $#$SMALL) {
189 3         258 $str = $SMALL -> [ $num[1] ];
190             } else {
191 16         1858 $str = $TENS -> [ $tens - 1];
192 16 100       2915 if ($ones > 0) {
193 15         2067 $str .= "-";
194 15         34 $str .= $SMALL -> [ $ones ];
195             }
196             }
197 19         533 push @str, $str;
198             }
199              
200 30         379 return join " and ", @str;
201             }
202              
203             1;
204              
205             __END__