File Coverage

blib/lib/Math/JS.pm
Criterion Covered Total %
statement 267 289 92.3
branch 148 210 70.4
condition 39 51 76.4
subroutine 47 47 100.0
pod 0 27 0.0
total 501 624 80.2


line stmt bran cond sub pod time code
1             # Checked against calculator at https://playcode.io/new
2             package Math::JS;
3 5     5   28236 use strict;
  5         8  
  5         161  
4 5     5   20 use warnings;
  5         8  
  5         266  
5 5     5   29 use Config;
  5         16  
  5         670  
6              
7             BEGIN {
8 5     5   16 $::bool = 0;
9 5         9 eval {require Math::Ryu;};
  5         750  
10 5 50 33     211 if(!$@ && $Math::Ryu::VERSION >= 0.06) {
11 0         0 $::bool = 1;
12             }
13             };
14 5     5   87 use 5.030; # avoid buggy floating-point assignments
  5         15  
15              
16             use overload
17 5         81 '+' => \&oload_add,
18             '-' => \&oload_sub,
19             '*' => \&oload_mul,
20             '/' => \&oload_div,
21             '%' => \&oload_mod,
22             '**' => \&oload_pow,
23             '++' => \&oload_inc,
24             '--' => \&oload_dec,
25             '>=' => \&oload_gte,
26             '<=' => \&oload_lte,
27             '==' => \&oload_equiv,
28             '!=' => \&oload_not_equiv,
29             '>' => \&oload_gt,
30             '<' => \&oload_lt,
31             '<=>' => \&oload_spaceship,
32             '""' => \&oload_stringify,
33             '&' => \&oload_and,
34             '|' => \&oload_ior,
35             '^' => \&oload_xor,
36             '~' => \&oload_not,
37             '<<' => \&oload_lshift,
38             '>>' => \&oload_rshift,
39 5     5   3157 ;
  5         9034  
40              
41 5     5   1372 use constant MAX_ULONG => 4294967295;
  5         7  
  5         500  
42 5     5   31 use constant MAX_SLONG => 2147483647;
  5         7  
  5         274  
43 5     5   23 use constant MIN_ULONG => 2147483648; # 1<<31 (Lowest positive value that sets 32nd bit)
  5         7  
  5         244  
44 5     5   23 use constant MIN_SLONG => -2147483648;
  5         6  
  5         244  
45 5     5   42 use constant LOW_31BIT => 1073741824; # 1<<30 (Lowest 31 bit positive number)
  5         6  
  5         249  
46 5     5   22 use constant MAX_NUM => 9007199254740991;
  5         7  
  5         267  
47 5     5   26 use constant USE_RYU => $::bool; # set in BEGIN{} block
  5         8  
  5         288  
48 5     5   23 use constant IVSIZE => $Config{ivsize};
  5         7  
  5         19437  
49              
50             our $VERSION = '0.05';
51              
52             require Exporter;
53             *import = \&Exporter::import;
54             @Math::JS::EXPORT = ();
55             @Math::JS::EXPORT_OK = qw(urs);
56              
57             require DynaLoader;
58             Math::JS->DynaLoader::bootstrap($VERSION);
59 5     5 0 1291 sub dl_load_flags {0}
60              
61             # The "type" of a Math::JS object's value will be either 'sint32', 'uint32'
62             # or 'number'. Roughly speaking, these types are (respectively) "unsigned
63             # 32-bit integer", "signed 32-bit integer" and "floating-point number".
64             # Hence, relying on the value returned by is_ok(), we have:
65             my %classify = (2 => 'uint32', 3 => 'sint32', 4 => 'number');
66              
67             sub new {
68 1484 50 33 1484 0 1741934 shift if(!ref($_[0]) && $_[0] eq "Math::JS"); # 'new' has been called as a method
69 1484         2380 my $val = shift;
70 1484         2568 my $ok = is_ok($val); # returns 1 for Math::JS object, 2 for a 32-bit UV,
71             # 3 for a 32-bit IV, 4 for an NV, and 0 (not ok) for anything else.
72 1484 100       2926 die "Bad argument (or no argument) given to new" unless $ok;
73              
74 1483 50       2641 if($ok == 1) {
75             # return a copy of the given Math::JS object
76 0         0 my $ret = shift;
77 0         0 return $ret;
78             }
79              
80 1483         5303 my %h = ('val' => $val, 'type' => $classify{$ok});
81 1483         6539 return bless(\%h, 'Math::JS');
82             }
83              
84             ########### + ##########
85             sub oload_add {
86 20 50   20 0 1725 die "Wrong number of arguments given to oload_add()"
87             if @_ > 3;
88              
89 20         42 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
90 20 50       39 die "Bad argument given to oload_add" unless $ok;
91              
92 20         30 my $ret1 = $_[0]->{val};
93              
94             return Math::JS->new($ret1 + $_[1]->{val})
95 20 100       55 if $ok == 1;
96              
97 4         13 return Math::JS->new($ret1 + $_[1]);
98             }
99              
100             ########### * ##########
101             sub oload_mul {
102 12 50   12 0 844 die "Wrong number of arguments given to oload_mul()"
103             if @_ > 3;
104              
105 12         25 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
106 12 50       21 die "Bad argument given to oload_mul" unless $ok;
107              
108 12         53 my $ret1 = $_[0]->{val};
109              
110             return Math::JS->new($ret1 * $_[1]->{val})
111 12 50       26 if $ok == 1;
112              
113 12         125 return Math::JS->new($ret1 * $_[1]);
114             }
115              
116             ########### - ##########
117             sub oload_sub {
118 3 50   3 0 956 die "Wrong number of arguments given to oload_sub()"
119             if @_ > 3;
120              
121 3         12 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
122 3 50       7 die "Bad argument given to oload_sub" unless $ok;
123              
124 3         6 my $third_arg = $_[2];
125              
126 3         6 my $ret0 = $_[0]->{val};
127              
128 3 50       8 if($ok == 1) {
129 0         0 return Math::JS->new($ret0 - $_[1]->{val});
130             }
131              
132 3 100       8 return Math::JS->new($_[1] - $ret0)
133             if $third_arg;
134 2         8 return Math::JS->new($ret0 - $_[1]);
135             }
136              
137             ########### / ##########
138             sub oload_div {
139 9 50   9 0 2062 die "Wrong number of arguments given to oload_div()"
140             if @_ > 3;
141              
142 9         28 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
143 9 50       22 die "Bad argument given to oload_div" unless $ok;
144              
145 9         27 my $third_arg = $_[2];
146 9         16 my ($num, $den, $ret);
147              
148 9 50       34 if($ok == 1) {
    100          
149 0         0 $num = $_[0]->{val};
150 0         0 $den = $_[1]->{val};
151             }
152             elsif($third_arg) {
153 1         1 $num = $_[1];
154 1         2 $den = $_[0]->{val};
155             }
156             else {
157 8         15 $num = $_[0]->{val};
158 8         14 $den = $_[1];
159             }
160              
161 9 100       23 if($den == 0) { $ret = $num < 0 ? _get_ninf() : _get_pinf() }
  2 100       10  
162 7         15 else { $ret = $num / $den }
163 9         24 return Math::JS->new($ret);
164             }
165              
166             ########### % ##########
167             sub oload_mod {
168 115 50   115 0 16759 die "Wrong number of arguments given to oload_mod()"
169             if @_ > 3;
170              
171 115         253 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
172 115 50       268 die "Bad argument given to oload_mod" unless $ok;
173              
174 115         196 my $third_arg = $_[2];
175 115         162 my ($num, $den);
176              
177 115 100       343 if($ok == 1) {
    100          
178 32         80 $num = $_[0]->{val};
179 32         59 $den = $_[1]->{val};
180             }
181             elsif($third_arg) {
182 29         45 $num = $_[1];
183 29         50 $den = $_[0]->{val};
184             }
185             else {
186 54         97 $num = $_[0]->{val};
187 54         89 $den = $_[1];
188             }
189              
190 115 100 100     214 return Math::JS->new(_get_nan())
      100        
191             if(_infnan($num) || _isnan($den) || $den == 0);
192 110 100 100     233 return Math::JS->new($num)
193             if(_isinf($den) || $num == 0);
194 106         435 return Math::JS->new(_fmod($num, $den));
195             }
196              
197             ########### ** ##########
198             sub oload_pow {
199 5 50   5 0 31 die "Wrong number of arguments given to oload_pow()"
200             if @_ > 3;
201              
202 5         14 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
203 5 50       16 die "Bad argument given to oload_div" unless $ok;
204              
205 5         10 my $third_arg = $_[2];
206              
207 5         11 my $val0 = $_[0]->{val};
208              
209 5 50       22 if($ok == 1) {
210 0 0       0 if($third_arg) {
211             return Math::JS->new(_get_nan())
212 0 0 0     0 if($_[1]->{val} < 0 && $val0 != int($val0));
213 0         0 return Math::JS->new($_[1]->{val} ** $val0);
214             }
215              
216             return Math::JS->new(_get_nan())
217 0 0 0     0 if($val0 < 0 && $_[1]->{val} != int($_[1]->{val}));
218 0         0 return Math::JS->new($val0 ** $_[1]->{val});
219             }
220              
221 5 100       48 return Math::JS->new($_[1] ** $val0)
222             if $third_arg;
223 4         21 return Math::JS->new($val0 ** $_[1]);
224             }
225              
226             ########### & ##########
227             sub oload_and {
228 36 50   36 0 1164 die "Wrong number of arguments given to oload_and()"
229             if @_ > 3;
230              
231 36         73 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
232 36 50       70 die "Bad argument given to oload_and" unless $ok;
233              
234 36         64 my $val0 = $_[0]->{val};
235             $val0 = reduce($val0)
236 36 100       112 if $_[0]->{type} eq 'number';
237              
238 36         46 my $retval;
239              
240 36 100       75 if($ok == 1) {
241 32         54 my $val1 = $_[1]->{val};
242             $val1 = reduce($val1)
243 32 100       81 if $_[1]->{type} eq 'number';
244              
245 32         86 $retval = ($val0 & 0xffffffff) & ($val1 & 0xffffffff);
246 32         136 return Math::JS->new(unpack 'l', pack 'L', $retval);
247             }
248              
249 4         6 my $val1 = $_[1];
250 4 50       39 $val1 = reduce($val1)
251             if $ok == 4;
252              
253 4         9 $retval = ($val0 & 0xffffffff) & ($val1 & 0xffffffff);
254 4         45 return Math::JS->new(unpack 'l', pack 'L', $retval);
255             }
256              
257             ########### | ##########
258             sub oload_ior {
259 32 50   32 0 878 die "Wrong number of arguments given to oload_ior()"
260             if @_ > 3;
261              
262 32         69 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
263 32 50       69 die "Bad argument given to oload_ior" unless $ok;
264              
265 32         57 my $val0 = $_[0]->{val};
266             $val0 = reduce($val0)
267 32 100       135 if $_[0]->{type} eq 'number';
268              
269 32         39 my $retval;
270              
271 32 100       64 if($ok == 1) {
272 28         40 my $val1 = $_[1]->{val};
273             $val1 = reduce($val1)
274 28 100       55 if $_[1]->{type} eq 'number';
275              
276 28         50 $retval = ($val0 & 0xffffffff) | ($val1 & 0xffffffff);
277 28         105 return Math::JS->new(unpack 'l', pack 'L', $retval);
278             }
279              
280 4         6 my $val1 = $_[1];
281 4 50       11 $val1 = reduce($val1)
282             if $ok == 4;
283              
284 4         10 $retval = ($val0 & 0xffffffff) | ($val1 & 0xffffffff);
285 4         22 return Math::JS->new(unpack 'l', pack 'L', $retval);
286             }
287              
288             ########### ^ ##########
289             sub oload_xor {
290 41 50   41 0 899 die "Wrong number of arguments given to oload_xor()"
291             if @_ > 3;
292              
293 41         81 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
294 41 50       69 die "Bad argument given to oload_xor" unless $ok;
295              
296 41         74 my $val0 = $_[0]->{val};
297             $val0 = reduce($val0)
298 41 100       101 if $_[0]->{type} eq 'number';
299              
300 41         48 my $retval;
301              
302 41 100       87 if($ok == 1) {
303 37         50 my $val1 = $_[1]->{val};
304             $val1 = reduce($val1)
305 37 100       69 if $_[1]->{type} eq 'number';
306              
307 37         59 $retval = ($val0 & 0xffffffff) ^ ($val1 & 0xffffffff);
308 37         141 return Math::JS->new(unpack 'l', pack 'L', $retval);
309             }
310              
311 4         12 my $val1 = $_[1];
312 4 50       12 $val1 = reduce($val1)
313             if $ok == 4;
314              
315 4         10 $retval = ($val0 & 0xffffffff) ^ ($val1 & 0xffffffff);
316 4         31 return Math::JS->new(unpack 'l', pack 'L', $retval);
317             }
318              
319             ########### ~ ##########
320             sub oload_not {
321 21 50   21 0 42 die "Wrong number of arguments given to oload_not()"
322             if @_ > 3;
323              
324 21         33 my $val = $_[0]->{val};
325             $val = reduce($val )
326 21 100       75 if $_[0]->{type} eq 'number';
327              
328 21 100       57 return Math::JS->new( MAX_ULONG - $val )
329             if($val > MAX_SLONG);
330              
331 17         38 return Math::JS->new( -$val - 1);
332             }
333              
334             ########### ++ ##########
335             sub oload_inc {
336 3     3 0 5099 $_[0]->{val} += 1;
337             # 'type' needs to be checked
338 3         12 $_[0]->{type} = $classify{is_ok($_[0]->{val})};
339             }
340              
341             ########### -- ##########
342             sub oload_dec {
343 3     3 0 5570 $_[0]->{val} -= 1;
344             # 'type' needs to be checked
345 3         13 $_[0]->{type} = $classify{is_ok($_[0]->{val})};
346             }
347              
348             ########### "" ##########
349             sub oload_stringify {
350 346     346 0 13349 my $val = $_[0]->{val};
351             # "l" is signed 32-bit integer; "L" is unsigned 32-bit integer.
352 346         600 my $ret;
353 346 100 100     1575 if ($_[0]->{type} eq 'sint32') { $ret = unpack("l", pack("L", $val)) }
  111 50       551  
    100          
    100          
    100          
354 0         0 elsif ($_[0]->{type} eq 'uint32') { $ret = unpack("L", pack("L", $val)) }
355             elsif (_isinf($val)) {
356 2 100       7 $ret = $val > 0 ? 'Infinity' : '-Infinity';
357             }
358             elsif(_isnan($val)) {
359 1         2 $ret = 'nan';
360             }
361             elsif ($val < 1e+21 && $val == int($val)) {
362 21         207 $ret = sprintf "%.21g", $val;
363             }
364             else {
365 211         342 if(USE_RYU) {
366             $ret = Math::Ryu::fmtpy(Math::Ryu::d2s($val));
367             }
368             else {
369 211         1862 $ret = sprintf "%.17g", $val;
370             }
371             }
372 346         2533 return "$ret";
373             }
374              
375             ########### >= ##########
376             sub oload_gte {
377 103 50   103 0 39600 die "Wrong number of arguments given to oload_gte()"
378             if @_ > 3;
379              
380 103         398 my $cmp = oload_spaceship($_[0], $_[1], $_[2]);
381              
382 103 100       260 return 0 if !defined $cmp;
383 100 50       347 return 1 if $cmp >= 0;
384 0         0 return 0;
385             }
386              
387             ########### <= ##########
388             sub oload_lte {
389 103 50   103 0 38916 die "Wrong number of arguments given to oload_lte()"
390             if @_ > 3;
391              
392 103         382 my $cmp = oload_spaceship($_[0], $_[1], $_[2]);
393 103 100       243 return 0 if !defined $cmp;
394 100 50       410 return 1 if $cmp <= 0;
395 0         0 return 0;
396             }
397              
398             ########### == ##########
399             sub oload_equiv {
400 541 50   541 0 173215 die "Wrong number of arguments given to oload_equiv()"
401             if @_ > 3;
402              
403 541         1448 my $cmp = oload_spaceship($_[0], $_[1], $_[2]);
404              
405 541 100       988 return 0 if !defined $cmp;
406 540 50       1377 return 1 if $cmp == 0;
407 0         0 return 0;
408             }
409              
410             ########### != ##########
411             sub oload_not_equiv {
412 3 50   3 0 1185 die "Wrong number of arguments given to oload_equiv()"
413             if @_ > 3;
414              
415 3         10 my $cmp = oload_spaceship($_[0], $_[1], $_[2]);
416              
417 3 50       10 return 1 if !defined($cmp);
418 0 0       0 return 0 if $cmp == 0;
419 0         0 return 1;
420             }
421              
422             ########### > ##########
423             sub oload_gt {
424 3 50   3 0 1417 die "Wrong number of arguments given to oload_gt()"
425             if @_ > 3;
426              
427 3         7 my $cmp = oload_spaceship($_[0], $_[1], $_[2]);
428              
429 3 50       7 return 0 if !defined $cmp;
430 0 0       0 return 1 if $cmp > 0;
431 0         0 return 0;
432             }
433              
434             ########### < ##########
435             sub oload_lt {
436 3 50   3 0 1354 die "Wrong number of arguments given to oload_lt()"
437             if @_ > 3;
438              
439 3         7 my $cmp = oload_spaceship($_[0], $_[1], $_[2]);
440              
441 3 50       7 return 0 if !defined $cmp;
442 0 0       0 return 1 if $cmp < 0;
443 0         0 return 0;
444             }
445              
446             ########### <=> ##########
447             sub oload_spaceship {
448 756 50   756 0 1340 die "Wrong number of arguments given to oload_spaceship()"
449             if @_ > 3;
450              
451 756         1571 my $ok = is_ok($_[1]); # check that 2nd arg is suitable.
452 756 50       1483 die "Bad argument given to oload_spaceship" unless $ok;
453              
454 756         1083 my $third_arg = $_[2];
455              
456 756 100       1326 if($ok == 1) {
457              
458 76 50       133 if($third_arg) {
459 0         0 return ($_[1]->{val} <=> $_[0]->{val});
460             }
461 76         198 return ($_[0]->{val} <=> $_[1]->{val});
462             }
463              
464 680         836 my $second = $_[1];
465              
466 680 100       1349 if($third_arg) {
467 6         12 return ($second <=> $_[0]->{val});
468             }
469 674         1555 return ($_[0]->{val} <=> $second);
470             }
471              
472             ########### << ##########
473             sub oload_lshift {
474 103 50   103 0 4918 die "Wrong number of arguments given to oload_lshift()"
475             if @_ > 3;
476              
477 103         155 my $shift = abs(int $_[1]) % 32;
478 103 100 100     214 $shift = 32 - $shift if ($_[1] < 0 && $shift);
479              
480 103         135 my $val = $_[0]->{val};
481             $val = reduce($val)
482 103 100       227 if $_[0]->{type} eq 'number';
483              
484 103         383 return Math::JS->new(unpack 'l', pack 'l', (($val & 0xffffffff) << $shift) );
485             }
486              
487             ########### >> ##########
488             sub oload_rshift {
489              
490             # From the javascript manual:
491             #
492             # Excess bits shifted off to the right are discarded, and copies of
493             # the leftmost bit are shifted in from the left. This operation is
494             # also called "sign-propagating right shift" or "arithmetic right
495             # shift", because the sign of the resulting number is the same as
496             # the sign of the first operand.
497             #
498              
499 254 50   254 0 7421 die "Wrong number of arguments given to oload_rshift()"
500             if @_ > 3;
501              
502 254         487 my $shift = abs(int $_[1]) % 32;
503 254 100 100     554 $shift = 32 - $shift if ($_[1] < 0 && $shift);
504 254         383 my $type = $_[0]->{type};
505              
506 254         356 my $val = $_[0]->{val};
507              
508 254 100       447 if($type eq 'number') {
509 89         155 $val = reduce($val);
510 89         159 $type = $classify{is_ok($val)}; # Determine whether it reduced
511             # to 'sint32' or 'uint32'.
512             }
513              
514 254 100 66     882 if($type eq 'uint32' || ($type eq 'sint32' && $val < 0)) { # Highest order bit is set
      100        
515 143         2315 my $ior = 0xffffffff ^ ((1 << (32 - $shift)) - 1);
516 143         526 my $val = unpack('l', pack 'L', $val) >> $shift;
517 143         212 $val |= $ior;
518 143         412 return Math::JS->new(unpack 'l', pack 'L', $val);
519             }
520              
521             # Highest order bit is unset
522 111         290 return Math::JS->new( ($val & 0xffffffff) >> ($_[1] % 32) );
523             }
524              
525             ########### >>> ##########
526             sub urs { # unsigned 32-bit right shift (JavaScript's '>>>' operator).
527             # This function is not available via overloading, and is
528             # exported only on request ie - use Math::JS qw(urs);
529              
530 115 50   115 0 586 die "Wrong number of arguments given to urs()"
531             unless @_ == 2;
532              
533             # 1st argument must be a Math::JS object
534 115 50       332 die "Bad first argument given to urs()"
535             unless ref($_[0]) eq 'Math::JS';
536              
537             # 2nd arg must be a perl number (IV or NV);
538 115 50       264 die "Bad second arg given to urs()"
539             unless is_ok($_[1]) > 1;
540              
541 115         295 my $shift = abs(int $_[1]) % 32;
542 115 100 66     393 $shift = 32 - $shift if ($_[1] < 0 && $shift);
543 115         241 my $type = $_[0]->{type};
544              
545 115         189 my $val = $_[0]->{val};
546              
547 115 100       241 if($type eq 'number') {
548 113         295 $val = reduce($val);
549             # $val is either type 'uint3' or 'sint32' ... doesn't matter here.
550             }
551              
552 115         373 $val = unpack('L', pack 'L', $val) >> $shift;
553 115         284 return Math::JS->new($val);
554             }
555              
556             ###########################
557             ###########################
558              
559             sub reduce {
560             # Reduce values that are greater than MAX_ULONG or
561             # less than MIN_SLONG to their appropriate value
562             # for use in bit manipulation operations, following
563             # the procedure used by javascipt.
564              
565 316     316 0 451 my $mul = 1;
566 316         485 my $big = shift;
567              
568 316 100       578 return 0 if _infnan($big);
569              
570 229         390 my $integer = int($big);
571 229 100 100     645 return $integer
572             if($integer <= MAX_ULONG && $integer >= MIN_SLONG);
573              
574 228 100       441 if($big < 0) {
575 108         142 $mul = -1;
576 108         150 $big = -$big;
577             }
578 228         352 my $max = shift;
579              
580 228         573 $big -= int($big / (2 ** 32)) * (2 ** 32);
581 228         856 my $ret = unpack 'l', pack 'l', $big;
582 228         432 return $ret * $mul;
583             }
584              
585             sub is_ok {
586 2772     2772 0 9948 my $val = shift;
587 2772         5381 my $ret = _is_ok($val);
588              
589 2772 100       5228 return $ret if $ret < 2;
590              
591 2550 100       5553 return 4 if $val != int($val); # Handles NaN
592 2047 100 100     6707 return 4 if ($val > 4294967295 || $val < -2147483648); # Handles +Inf and -Inf
593 1820 100       3397 return 2 if $val >= 2147483648;
594 1738         5857 return 3; # This is the only remaining possibility
595             }
596              
597             sub _infnan {
598 434     434   1996 my $val = shift;
599 434         637 my $inf = 2 ** 1500;
600 434 100 100     2179 return 1 if ($val == $inf || $val == -$inf || $val != $val);
      100        
601 342         884 return 0;
602             }
603              
604             sub _isnan {
605 350     350   512 my $val = shift;
606 350 100       871 return 1 if $val != $val;
607 344         1742 return 0;
608             }
609              
610             sub _isinf {
611 345     345   575 my $val = shift;
612 345 100       1006 return 1 if abs($val) == 2 ** 1500;
613 340         1020 return 0;
614             }
615              
616             sub _get_pinf { # Return Math::JS object with a value of +Inf
617 2     2   6 return 2 ** 1500;
618             }
619              
620             sub _get_ninf { # Return Math::JS object with a value of -Inf
621 2     2   6 return -(2 ** 1500);
622             }
623              
624             sub _get_nan {
625 6     6   11 my $inf = 2 ** 1500;
626 6         22 return ($inf / $inf);
627             }
628              
629             1;
630              
631             __END__