This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade to Net::Ping 2.14.
[perl5.git] / lib / Math / BigRat.pm
CommitLineData
184f15d5
JH
1#!/usr/bin/perl -w
2
3# The following hash values are used:
4# sign : +,-,NaN,+inf,-inf
5# _d : denominator
6# _n : numeraotr (value = _n/_d)
7# _a : accuracy
8# _p : precision
bce7c187 9# _f : flags, used by MBR to flag parts of a rational as untouchable
184f15d5
JH
10
11package Math::BigRat;
12
13require 5.005_02;
14use strict;
15
16use Exporter;
17use Math::BigFloat;
18use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK $upgrade $downgrade
19 $accuracy $precision $round_mode $div_scale);
20
21@ISA = qw(Exporter Math::BigFloat);
22@EXPORT_OK = qw();
23
8f675a64 24$VERSION = '0.05';
184f15d5
JH
25
26use overload; # inherit from Math::BigFloat
27
28##############################################################################
29# global constants, flags and accessory
30
31use constant MB_NEVER_ROUND => 0x0001;
32
33$accuracy = $precision = undef;
34$round_mode = 'even';
35$div_scale = 40;
36$upgrade = undef;
37$downgrade = undef;
38
39my $nan = 'NaN';
40my $class = 'Math::BigRat';
41
8f675a64
JH
42sub isa
43 {
44 return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't
45 UNIVERSAL::isa(@_);
46 }
47
184f15d5
JH
48sub _new_from_float
49 {
bce7c187 50 # turn a single float input into a rational (like '0.1')
184f15d5
JH
51 my ($self,$f) = @_;
52
53 return $self->bnan() if $f->is_nan();
54 return $self->binf('-inf') if $f->{sign} eq '-inf';
55 return $self->binf('+inf') if $f->{sign} eq '+inf';
56
57 #print "f $f caller", join(' ',caller()),"\n";
58 $self->{_n} = $f->{_m}->copy(); # mantissa
59 $self->{_d} = Math::BigInt->bone();
60 $self->{sign} = $f->{sign}; $self->{_n}->{sign} = '+';
61 if ($f->{_e}->{sign} eq '-')
62 {
63 # something like Math::BigRat->new('0.1');
64 $self->{_d}->blsft($f->{_e}->copy()->babs(),10); # 1 / 1 => 1/10
65 }
66 else
67 {
68 # something like Math::BigRat->new('10');
69 # 1 / 1 => 10/1
70 $self->{_n}->blsft($f->{_e},10) unless $f->{_e}->is_zero();
71 }
72# print "float new $self->{_n} / $self->{_d}\n";
73 $self;
74 }
75
76sub new
77 {
78 # create a Math::BigRat
79 my $class = shift;
80
81 my ($n,$d) = shift;
82
83 my $self = { }; bless $self,$class;
84
85# print "ref ",ref($d),"\n";
86# if (ref($d))
87# {
88# print "isa float ",$d->isa('Math::BigFloat'),"\n";
89# print "isa int ",$d->isa('Math::BigInt'),"\n";
90# print "isa rat ",$d->isa('Math::BigRat'),"\n";
91# }
92
93 # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet
94
95 if ((ref $n) && (!$n->isa('Math::BigRat')))
96 {
97# print "is ref, but not rat\n";
98 if ($n->isa('Math::BigFloat'))
99 {
8f675a64 100 # print "is ref, and float\n";
184f15d5
JH
101 return $self->_new_from_float($n)->bnorm();
102 }
103 if ($n->isa('Math::BigInt'))
104 {
105# print "is ref, and int\n";
8f675a64
JH
106 $self->{_n} = $n->copy(); # "mantissa" = $n
107 $self->{_d} = Math::BigInt->bone();
108 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
109 return $self->bnorm();
110 }
111 if ($n->isa('Math::BigInt::Lite'))
112 {
113# print "is ref, and lite\n";
114 $self->{_n} = Math::BigInt->new($$n); # "mantissa" = $n
184f15d5
JH
115 $self->{_d} = Math::BigInt->bone();
116 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
117 return $self->bnorm();
118 }
119 }
120 return $n->copy() if ref $n;
121
122# print "is string\n";
123
124 if (!defined $n)
125 {
126 $self->{_n} = Math::BigInt->bzero(); # undef => 0
127 $self->{_d} = Math::BigInt->bone();
128 $self->{sign} = '+';
129 return $self->bnorm();
130 }
131 # string input with / delimiter
132 if ($n =~ /\s*\/\s*/)
133 {
134 return Math::BigRat->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid
135 return Math::BigRat->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid
136 ($n,$d) = split (/\//,$n);
137 # try as BigFloats first
138 if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/))
139 {
140 # one of them looks like a float
141 $self->_new_from_float(Math::BigFloat->new($n));
142 # now correct $self->{_n} due to $n
143 my $f = Math::BigFloat->new($d);
144 if ($f->{_e}->{sign} eq '-')
145 {
146 # 10 / 0.1 => 100/1
147 $self->{_n}->blsft($f->{_e}->copy()->babs(),10);
148 }
149 else
150 {
151 $self->{_d}->blsft($f->{_e},10); # 1 / 1 => 10/1
152 }
153 }
154 else
155 {
156 $self->{_n} = Math::BigInt->new($n);
157 $self->{_d} = Math::BigInt->new($d);
158 return $self->bnan() if $self->{_n}->is_nan() || $self->{_d}->is_nan();
159 # inf handling is missing here
160
161 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
162 # if $d is negative, flip sign
163 $self->{sign} =~ tr/+-/-+/ if $self->{_d}->{sign} eq '-';
164 $self->{_d}->{sign} = '+'; # normalize
165 }
166 return $self->bnorm();
167 }
168
169 # simple string input
170 if (($n =~ /[\.eE]/))
171 {
172 # looks like a float
173# print "float-like string $d\n";
174 $self->_new_from_float(Math::BigFloat->new($n));
175 }
176 else
177 {
178 $self->{_n} = Math::BigInt->new($n);
179 $self->{_d} = Math::BigInt->bone();
180 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
181 }
182 $self->bnorm();
183 }
184
8f675a64
JH
185###############################################################################
186
184f15d5
JH
187sub bstr
188 {
189 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
190
191 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
192 {
193 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
194 return $s;
195 }
196
197# print "bstr $x->{sign} $x->{_n} $x->{_d}\n";
198 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
199
200 return $s.$x->{_n}->bstr() if $x->{_d}->is_one();
201 return $s.$x->{_n}->bstr() . '/' . $x->{_d}->bstr();
202 }
203
204sub bsstr
205 {
206 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
207
208 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
209 {
210 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
211 return $s;
212 }
213
214 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
215 return $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
216 }
217
218sub bnorm
219 {
220 # reduce the number to the shortest form and remember this (so that we
221 # don't reduce again)
222 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
223
224 # this is to prevent automatically rounding when MBI's globals are set
225 $x->{_d}->{_f} = MB_NEVER_ROUND;
226 $x->{_n}->{_f} = MB_NEVER_ROUND;
227 # 'forget' that parts were rounded via MBI::bround() in MBF's bfround()
228 $x->{_d}->{_a} = undef; $x->{_n}->{_a} = undef;
229 $x->{_d}->{_p} = undef; $x->{_n}->{_p} = undef;
230
231 # normalize zeros to 0/1
232 if (($x->{sign} =~ /^[+-]$/) &&
233 ($x->{_n}->is_zero()))
234 {
235 $x->{sign} = '+'; # never -0
236 $x->{_d} = Math::BigInt->bone() unless $x->{_d}->is_one();
237 return $x;
238 }
239
240# print "$x->{_n} / $x->{_d} => ";
241 # reduce other numbers
8f675a64
JH
242 # print "bgcd $x->{_n} (",ref($x->{_n}),") $x->{_d} (",ref($x->{_d}),")\n";
243 # disable upgrade in BigInt, otherwise deep recursion
244 local $Math::BigInt::upgrade = undef;
184f15d5
JH
245 my $gcd = $x->{_n}->bgcd($x->{_d});
246
247 if (!$gcd->is_one())
248 {
249 $x->{_n}->bdiv($gcd);
250 $x->{_d}->bdiv($gcd);
251 }
252# print "$x->{_n} / $x->{_d}\n";
253 $x;
254 }
255
256##############################################################################
257# special values
258
259sub _bnan
260 {
261 # used by parent class bone() to initialize number to 1
262 my $self = shift;
263 $self->{_n} = Math::BigInt->bzero();
264 $self->{_d} = Math::BigInt->bzero();
265 }
266
267sub _binf
268 {
269 # used by parent class bone() to initialize number to 1
270 my $self = shift;
271 $self->{_n} = Math::BigInt->bzero();
272 $self->{_d} = Math::BigInt->bzero();
273 }
274
275sub _bone
276 {
277 # used by parent class bone() to initialize number to 1
278 my $self = shift;
279 $self->{_n} = Math::BigInt->bone();
280 $self->{_d} = Math::BigInt->bone();
281 }
282
283sub _bzero
284 {
285 # used by parent class bone() to initialize number to 1
286 my $self = shift;
287 $self->{_n} = Math::BigInt->bzero();
288 $self->{_d} = Math::BigInt->bone();
289 }
290
291##############################################################################
292# mul/add/div etc
293
294sub badd
295 {
bce7c187 296 # add two rationals
184f15d5
JH
297 my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
298
8f675a64
JH
299 $x = $class->new($x) unless $x->isa($class);
300 $y = $class->new($y) unless $y->isa($class);
184f15d5 301
8f675a64 302 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
184f15d5
JH
303
304 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
305 # - + - = --------- = --
306 # 4 3 4*3 12
307
308 my $gcd = $x->{_d}->bgcd($y->{_d});
309
310 my $aa = $x->{_d}->copy();
311 my $bb = $y->{_d}->copy();
312 if ($gcd->is_one())
313 {
314 $bb->bdiv($gcd); $aa->bdiv($gcd);
315 }
316 $x->{_n}->bmul($bb); $x->{_n}->{sign} = $x->{sign};
317 my $m = $y->{_n}->copy()->bmul($aa);
318 $m->{sign} = $y->{sign}; # 2/1 - 2/1
319 $x->{_n}->badd($m);
320
321 $x->{_d}->bmul($y->{_d});
322
323 # calculate new sign
324 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
325
326 $x->bnorm()->round($a,$p,$r);
327 }
328
329sub bsub
330 {
bce7c187 331 # subtract two rationals
184f15d5
JH
332 my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
333
8f675a64
JH
334 $x = $class->new($x) unless $x->isa($class);
335 $y = $class->new($y) unless $y->isa($class);
336
184f15d5
JH
337 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
338 # TODO: inf handling
339
184f15d5
JH
340 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
341 # - + - = --------- = --
342 # 4 3 4*3 12
343
344 my $gcd = $x->{_d}->bgcd($y->{_d});
345
346 my $aa = $x->{_d}->copy();
347 my $bb = $y->{_d}->copy();
348 if ($gcd->is_one())
349 {
350 $bb->bdiv($gcd); $aa->bdiv($gcd);
351 }
352 $x->{_n}->bmul($bb); $x->{_n}->{sign} = $x->{sign};
353 my $m = $y->{_n}->copy()->bmul($aa);
354 $m->{sign} = $y->{sign}; # 2/1 - 2/1
355 $x->{_n}->bsub($m);
356
357 $x->{_d}->bmul($y->{_d});
358
359 # calculate new sign
360 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
361
362 $x->bnorm()->round($a,$p,$r);
363 }
364
365sub bmul
366 {
bce7c187 367 # multiply two rationals
184f15d5
JH
368 my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
369
8f675a64
JH
370 $x = $class->new($x) unless $x->isa($class);
371 $y = $class->new($y) unless $y->isa($class);
372
184f15d5
JH
373 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
374
375 # inf handling
376 if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
377 {
378 return $x->bnan() if $x->is_zero() || $y->is_zero();
379 # result will always be +-inf:
380 # +inf * +/+inf => +inf, -inf * -/-inf => +inf
381 # +inf * -/-inf => -inf, -inf * +/+inf => -inf
382 return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/);
383 return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
384 return $x->binf('-');
385 }
386
387 # x== 0 # also: or y == 1 or y == -1
388 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
389
184f15d5
JH
390 # According to Knuth, this can be optimized by doingtwice gcd (for d and n)
391 # and reducing in one step)
392
393 # 1 1 2 1
394 # - * - = - = -
395 # 4 3 12 6
396 $x->{_n}->bmul($y->{_n});
397 $x->{_d}->bmul($y->{_d});
398
399 # compute new sign
400 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
401
402 $x->bnorm()->round($a,$p,$r);
403 }
404
405sub bdiv
406 {
407 # (dividend: BRAT or num_str, divisor: BRAT or num_str) return
408 # (BRAT,BRAT) (quo,rem) or BRAT (only rem)
409 my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
410
8f675a64
JH
411 $x = $class->new($x) unless $x->isa($class);
412 $y = $class->new($y) unless $y->isa($class);
413
184f15d5
JH
414 return $self->_div_inf($x,$y)
415 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
416
417 # x== 0 # also: or y == 1 or y == -1
418 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
419
420 # TODO: list context, upgrade
421
184f15d5
JH
422 # 1 1 1 3
423 # - / - == - * -
424 # 4 3 4 1
425 $x->{_n}->bmul($y->{_d});
426 $x->{_d}->bmul($y->{_n});
427
428 # compute new sign
429 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
430
431 $x->bnorm()->round($a,$p,$r);
432 }
433
434##############################################################################
435# is_foo methods (the rest is inherited)
436
437sub is_int
438 {
439 # return true if arg (BRAT or num_str) is an integer
440 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
441
442 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
443 $x->{_d}->is_one(); # 1e-1 => no integer
444 0;
445 }
446
447sub is_zero
448 {
449 # return true if arg (BRAT or num_str) is zero
450 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
451
452 return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero();
453 0;
454 }
455
456sub is_one
457 {
458 # return true if arg (BRAT or num_str) is +1 or -1 if signis given
459 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
460
461 my $sign = shift || ''; $sign = '+' if $sign ne '-';
462 return 1
463 if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one());
464 0;
465 }
466
467sub is_odd
468 {
469 # return true if arg (BFLOAT or num_str) is odd or false if even
470 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
471
472 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
473 ($x->{_d}->is_one() && $x->{_n}->is_odd()); # x/2 is not, but 3/1
474 0;
475 }
476
477sub is_even
478 {
479 # return true if arg (BINT or num_str) is even or false if odd
480 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
481
482 return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
483 return 1 if ($x->{_d}->is_one() # x/3 is never
484 && $x->{_n}->is_even()); # but 4/1 is
485 0;
486 }
487
488BEGIN
489 {
490 *objectify = \&Math::BigInt::objectify;
491 }
492
493##############################################################################
494# parts() and friends
495
496sub numerator
497 {
498 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
499
500 my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign};
501 $n;
502 }
503
504sub denominator
505 {
506 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
507
508 $x->{_d}->copy();
509 }
510
511sub parts
512 {
513 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
514
515 my $n = $x->{_n}->copy();
516 $n->{sign} = $x->{sign};
517 return ($x->{_n}->copy(),$x->{_d}->copy());
518 }
519
520sub length
521 {
522 return 0;
523 }
524
525sub digit
526 {
527 return 0;
528 }
529
530##############################################################################
531# special calc routines
532
533sub bceil
534 {
535 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
536
537 return $x unless $x->{sign} =~ /^[+-]$/;
538 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
539
540 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1
541 $x->{_d}->bone();
542 $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1
543 $x;
544 }
545
546sub bfloor
547 {
548 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
549
550 return $x unless $x->{sign} =~ /^[+-]$/;
551 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
552
553 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1
554 $x->{_d}->bone();
555 $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
556 $x;
557 }
558
559sub bfac
560 {
561 return Math::BigRat->bnan();
562 }
563
564sub bpow
565 {
566 my ($self,$x,$y,@r) = objectify(2,@_);
567
568 return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
569 return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
570 return $x->bone(@r) if $y->is_zero();
571 return $x->round(@r) if $x->is_one() || $y->is_one();
572 if ($x->{sign} eq '-' && $x->{_n}->is_one() && $x->{_d}->is_one())
573 {
574 # if $x == -1 and odd/even y => +1/-1
575 return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
576 # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1;
577 }
578 # 1 ** -y => 1 / (1 ** |y|)
579 # so do test for negative $y after above's clause
580 # return $x->bnan() if $y->{sign} eq '-';
581 return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0)
582
583 my $pow2 = $self->__one();
584 my $y1 = Math::BigInt->new($y->{_n}/$y->{_d})->babs();
585 my $two = Math::BigInt->new(2);
586 while (!$y1->is_one())
587 {
588 print "at $y1 (= $x)\n";
589 $pow2->bmul($x) if $y1->is_odd();
590 $y1->bdiv($two);
591 $x->bmul($x);
592 }
593 $x->bmul($pow2) unless $pow2->is_one();
594 # n ** -x => 1/n ** x
595 ($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-';
596 $x;
597 #$x->round(@r);
598 }
599
600sub blog
601 {
602 return Math::BigRat->bnan();
603 }
604
605sub bsqrt
606 {
607 my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
608
609 return $x->bnan() if $x->{sign} ne '+'; # inf, NaN, -1 etc
610 $x->{_d}->bsqrt($a,$p,$r);
611 $x->{_n}->bsqrt($a,$p,$r);
612 $x->bnorm();
613 }
614
615sub blsft
616 {
617 my ($self,$x,$y,$b,$a,$p,$r) = objectify(3,@_);
618
619 $x->bmul( $b->copy()->bpow($y), $a,$p,$r);
620 $x;
621 }
622
623sub brsft
624 {
625 my ($self,$x,$y,$b,$a,$p,$r) = objectify(2,@_);
626
627 $x->bdiv( $b->copy()->bpow($y), $a,$p,$r);
628 $x;
629 }
630
631##############################################################################
632# round
633
634sub round
635 {
636 $_[0];
637 }
638
639sub bround
640 {
641 $_[0];
642 }
643
644sub bfround
645 {
646 $_[0];
647 }
648
649##############################################################################
650# comparing
651
652sub bcmp
653 {
654 my ($self,$x,$y) = objectify(2,@_);
655
656 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
657 {
658 # handle +-inf and NaN
659 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
660 return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/;
661 return +1 if $x->{sign} eq '+inf';
662 return -1 if $x->{sign} eq '-inf';
663 return -1 if $y->{sign} eq '+inf';
664 return +1;
665 }
666 # check sign for speed first
667 return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y
668 return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0
669
670 # shortcut
671 my $xz = $x->{_n}->is_zero();
672 my $yz = $y->{_n}->is_zero();
673 return 0 if $xz && $yz; # 0 <=> 0
674 return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y
675 return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0
676
677 my $t = $x->{_n} * $y->{_d}; $t->{sign} = $x->{sign};
678 my $u = $y->{_n} * $x->{_d}; $u->{sign} = $y->{sign};
679 $t->bcmp($u);
680 }
681
682sub bacmp
683 {
684 my ($self,$x,$y) = objectify(2,@_);
685
686 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
687 {
688 # handle +-inf and NaN
689 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
690 return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
691 return +1; # inf is always bigger
692 }
693
694 my $t = $x->{_n} * $y->{_d};
695 my $u = $y->{_n} * $x->{_d};
696 $t->bacmp($u);
697 }
698
699##############################################################################
700# output conversation
701
702sub as_number
703 {
704 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
705
706 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc
707 my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3
708 $t->{sign} = $x->{sign};
709 $t;
710 }
711
712#sub import
713# {
714# my $self = shift;
715# Math::BigInt->import(@_);
716# $self->SUPER::import(@_); # need it for subclasses
717# #$self->export_to_level(1,$self,@_); # need this ?
718# }
719
7201;
721
722__END__
723
724=head1 NAME
725
bce7c187 726Math::BigRat - arbitrarily big rationals
184f15d5
JH
727
728=head1 SYNOPSIS
729
730 use Math::BigRat;
731
732 $x = Math::BigRat->new('3/7');
733
734 print $x->bstr(),"\n";
735
736=head1 DESCRIPTION
737
738This is just a placeholder until the real thing is up and running. Watch this
739space...
740
741=head2 MATH LIBRARY
742
743Math with the numbers is done (by default) by a module called
744Math::BigInt::Calc. This is equivalent to saying:
745
746 use Math::BigRat lib => 'Calc';
747
748You can change this by using:
749
750 use Math::BigRat lib => 'BitVect';
751
752The following would first try to find Math::BigInt::Foo, then
753Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc:
754
755 use Math::BigRat lib => 'Foo,Math::BigInt::Bar';
756
757Calc.pm uses as internal format an array of elements of some decimal base
758(usually 1e7, but this might be differen for some systems) with the least
759significant digit first, while BitVect.pm uses a bit vector of base 2, most
760significant bit first. Other modules might use even different means of
761representing the numbers. See the respective module documentation for further
762details.
763
764=head1 METHODS
765
766=head2 new
767
768 $x = Math::BigRat->new('1/3');
769
770Create a new Math::BigRat object. Input can come in various forms:
771
772 $x = Math::BigRat->new('1/3'); # simple string
773 $x = Math::BigRat->new('1 / 3'); # spaced
774 $x = Math::BigRat->new('1 / 0.1'); # w/ floats
775 $x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt
776 $x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat
777
778=head2 numerator
779
780 $n = $x->numerator();
781
782Returns a copy of the numerator (the part above the line) as signed BigInt.
783
784=head2 denominator
785
786 $d = $x->denominator();
787
788Returns a copy of the denominator (the part under the line) as positive BigInt.
789
790=head2 parts
791
792 ($n,$d) = $x->parts();
793
794Return a list consisting of (signed) numerator and (unsigned) denominator as
795BigInts.
796
797=head1 BUGS
798
799None know yet. Please see also L<Math::BigInt>.
800
801=head1 LICENSE
802
803This program is free software; you may redistribute it and/or modify it under
804the same terms as Perl itself.
805
806=head1 SEE ALSO
807
808L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>,
809L<Math::BigInt::Pari> and L<Math::BigInt::GMP>.
810
811The package at
812L<http://search.cpan.org/search?mode=module&query=Math%3A%3ABigRat> may
813contain more documentation and examples as well as testcases.
814
815=head1 AUTHORS
816
817(C) by Tels L<http://bloodgate.com/> 2001-2002.
818
819=cut