3 # "Tax the rat farms." - Lord Vetinari
6 # The following hash values are used:
7 # sign : +,-,NaN,+inf,-inf
9 # _n : numeraotr (value = _n/_d)
12 # _f : flags, used by MBR to flag parts of a rationale as untouchable
21 use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK $upgrade $downgrade
22 $accuracy $precision $round_mode $div_scale);
24 @ISA = qw(Exporter Math::BigFloat);
29 use overload; # inherit from Math::BigFloat
31 ##############################################################################
32 # global constants, flags and accessory
34 use constant MB_NEVER_ROUND => 0x0001;
36 $accuracy = $precision = undef;
43 my $class = 'Math::BigRat';
44 my $MBI = 'Math::BigInt';
48 return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't
54 # turn a single float input into a rationale (like '0.1')
57 return $self->bnan() if $f->is_nan();
58 return $self->binf('-inf') if $f->{sign} eq '-inf';
59 return $self->binf('+inf') if $f->{sign} eq '+inf';
61 $self->{_n} = $f->{_m}->copy(); # mantissa
62 $self->{_d} = $MBI->bone();
63 $self->{sign} = $f->{sign}; $self->{_n}->{sign} = '+';
64 if ($f->{_e}->{sign} eq '-')
66 # something like Math::BigRat->new('0.1');
67 $self->{_d}->blsft($f->{_e}->copy()->babs(),10); # 1 / 1 => 1/10
71 # something like Math::BigRat->new('10');
73 $self->{_n}->blsft($f->{_e},10) unless $f->{_e}->is_zero();
80 # create a Math::BigRat
85 my $self = { }; bless $self,$class;
87 # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet
89 if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat')))
91 if ($n->isa('Math::BigFloat'))
93 return $self->_new_from_float($n)->bnorm();
95 if ($n->isa('Math::BigInt'))
97 $self->{_n} = $n->copy(); # "mantissa" = $n
98 $self->{_d} = $MBI->bone();
99 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
100 return $self->bnorm();
102 if ($n->isa('Math::BigInt::Lite'))
104 $self->{_n} = $MBI->new($$n,undef,undef); # "mantissa" = $n
105 $self->{_d} = $MBI->bone();
106 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
107 return $self->bnorm();
110 return $n->copy() if ref $n;
114 $self->{_n} = $MBI->bzero(); # undef => 0
115 $self->{_d} = $MBI->bone();
117 return $self->bnorm();
119 # string input with / delimiter
120 if ($n =~ /\s*\/\s*/)
122 return Math::BigRat->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid
123 return Math::BigRat->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid
124 ($n,$d) = split (/\//,$n);
125 # try as BigFloats first
126 if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/))
128 # one of them looks like a float
129 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so:
130 local $Math::BigFloat::accuracy = undef;
131 local $Math::BigFloat::precision = undef;
132 local $Math::BigInt::accuracy = undef;
133 local $Math::BigInt::precision = undef;
134 $self->_new_from_float(Math::BigFloat->new($n));
135 # now correct $self->{_n} due to $n
136 my $f = Math::BigFloat->new($d,undef,undef);
137 if ($f->{_e}->{sign} eq '-')
140 $self->{_n}->blsft($f->{_e}->copy()->babs(),10);
144 $self->{_d}->blsft($f->{_e},10); # 1 / 1 => 10/1
149 # both d and n are (big)ints
150 $self->{_n} = $MBI->new($n,undef,undef);
151 $self->{_d} = $MBI->new($d,undef,undef);
152 return $self->bnan() if $self->{_n}->is_nan() || $self->{_d}->is_nan();
153 # inf handling is missing here
155 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
156 # if $d is negative, flip sign
157 $self->{sign} =~ tr/+-/-+/ if $self->{_d}->{sign} eq '-';
158 $self->{_d}->{sign} = '+'; # normalize
160 return $self->bnorm();
163 # simple string input
164 if (($n =~ /[\.eE]/))
166 # looks like a float, quacks like a float, so probably is a float
167 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so:
168 local $Math::BigFloat::accuracy = undef;
169 local $Math::BigFloat::precision = undef;
170 local $Math::BigInt::accuracy = undef;
171 local $Math::BigInt::precision = undef;
172 $self->_new_from_float(Math::BigFloat->new($n,undef,undef));
176 $self->{_n} = $MBI->new($n,undef,undef);
177 $self->{_d} = $MBI->bone();
178 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
179 return $self->bnan() if $self->{sign} eq 'NaN';
180 return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/;
185 ###############################################################################
189 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
191 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
193 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
197 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
199 return $s.$x->{_n}->bstr() if $x->{_d}->is_one();
200 return $s.$x->{_n}->bstr() . '/' . $x->{_d}->bstr();
205 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
207 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
209 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
213 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
214 return $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
219 # reduce the number to the shortest form and remember this (so that we
220 # don't reduce again)
221 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
223 # both parts must be BigInt's
224 die ("n is not $MBI but (".ref($x->{_n}).')')
225 if ref($x->{_n}) ne $MBI;
226 die ("d is not $MBI but (".ref($x->{_d}).')')
227 if ref($x->{_d}) ne $MBI;
229 # this is to prevent automatically rounding when MBI's globals are set
230 $x->{_d}->{_f} = MB_NEVER_ROUND;
231 $x->{_n}->{_f} = MB_NEVER_ROUND;
232 # 'forget' that parts were rounded via MBI::bround() in MBF's bfround()
233 $x->{_d}->{_a} = undef; $x->{_n}->{_a} = undef;
234 $x->{_d}->{_p} = undef; $x->{_n}->{_p} = undef;
236 # no normalize for NaN, inf etc.
237 return $x if $x->{sign} !~ /^[+-]$/;
239 # normalize zeros to 0/1
240 if (($x->{sign} =~ /^[+-]$/) &&
241 ($x->{_n}->is_zero()))
243 $x->{sign} = '+'; # never -0
244 $x->{_d} = $MBI->bone() unless $x->{_d}->is_one();
248 return $x if $x->{_d}->is_one(); # no need to reduce
250 # reduce other numbers
251 # disable upgrade in BigInt, otherwise deep recursion
252 local $Math::BigInt::upgrade = undef;
253 local $Math::BigInt::accuracy = undef;
254 local $Math::BigInt::precision = undef;
255 my $gcd = $x->{_n}->bgcd($x->{_d});
259 $x->{_n}->bdiv($gcd);
260 $x->{_d}->bdiv($gcd);
265 ##############################################################################
270 # used by parent class bone() to initialize number to 1
272 $self->{_n} = $MBI->bzero();
273 $self->{_d} = $MBI->bzero();
278 # used by parent class bone() to initialize number to +inf/-inf
280 $self->{_n} = $MBI->bzero();
281 $self->{_d} = $MBI->bzero();
286 # used by parent class bone() to initialize number to +1/-1
288 $self->{_n} = $MBI->bone();
289 $self->{_d} = $MBI->bone();
294 # used by parent class bone() to initialize number to 0
296 $self->{_n} = $MBI->bzero();
297 $self->{_d} = $MBI->bone();
300 ##############################################################################
308 my ($self,$x,$y,@r) = (ref($_[0]),@_);
309 # objectify is costly, so avoid it
310 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
312 ($self,$x,$y,@r) = objectify(2,@_);
315 $x = $self->new($x) unless $x->isa($self);
316 $y = $self->new($y) unless $y->isa($self);
318 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
321 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
322 # - + - = --------- = --
325 # we do not compute the gcd() here, but simple do:
327 # - + - = --------- = --
330 # the gcd() calculation and reducing is then done in bnorm()
332 local $Math::BigInt::accuracy = undef;
333 local $Math::BigInt::precision = undef;
335 $x->{_n}->bmul($y->{_d}); $x->{_n}->{sign} = $x->{sign};
336 my $m = $y->{_n}->copy()->bmul($x->{_d});
337 $m->{sign} = $y->{sign}; # 2/1 - 2/1
340 $x->{_d}->bmul($y->{_d});
343 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
345 $x->bnorm()->round(@r);
350 # subtract two rationales
353 my ($self,$x,$y,@r) = (ref($_[0]),@_);
354 # objectify is costly, so avoid it
355 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
357 ($self,$x,$y,@r) = objectify(2,@_);
360 $x = $class->new($x) unless $x->isa($class);
361 $y = $class->new($y) unless $y->isa($class);
363 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
366 # 1 1 gcd(3,4) = 1 1*3 - 1*4 7
367 # - - - = --------- = --
370 # we do not compute the gcd() here, but simple do:
372 # - - - = --------- = - --
375 local $Math::BigInt::accuracy = undef;
376 local $Math::BigInt::precision = undef;
378 $x->{_n}->bmul($y->{_d}); $x->{_n}->{sign} = $x->{sign};
379 my $m = $y->{_n}->copy()->bmul($x->{_d});
380 $m->{sign} = $y->{sign}; # 2/1 - 2/1
383 $x->{_d}->bmul($y->{_d});
386 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
388 $x->bnorm()->round(@r);
393 # multiply two rationales
396 my ($self,$x,$y,@r) = (ref($_[0]),@_);
397 # objectify is costly, so avoid it
398 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
400 ($self,$x,$y,@r) = objectify(2,@_);
403 $x = $class->new($x) unless $x->isa($class);
404 $y = $class->new($y) unless $y->isa($class);
406 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
409 if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
411 return $x->bnan() if $x->is_zero() || $y->is_zero();
412 # result will always be +-inf:
413 # +inf * +/+inf => +inf, -inf * -/-inf => +inf
414 # +inf * -/-inf => -inf, -inf * +/+inf => -inf
415 return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/);
416 return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
417 return $x->binf('-');
420 # x== 0 # also: or y == 1 or y == -1
421 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
423 # According to Knuth, this can be optimized by doingtwice gcd (for d and n)
424 # and reducing in one step)
430 local $Math::BigInt::accuracy = undef;
431 local $Math::BigInt::precision = undef;
432 $x->{_n}->bmul($y->{_n});
433 $x->{_d}->bmul($y->{_d});
436 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
438 $x->bnorm()->round(@r);
443 # (dividend: BRAT or num_str, divisor: BRAT or num_str) return
444 # (BRAT,BRAT) (quo,rem) or BRAT (only rem)
447 my ($self,$x,$y,@r) = (ref($_[0]),@_);
448 # objectify is costly, so avoid it
449 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
451 ($self,$x,$y,@r) = objectify(2,@_);
454 $x = $class->new($x) unless $x->isa($class);
455 $y = $class->new($y) unless $y->isa($class);
457 return $self->_div_inf($x,$y)
458 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
460 # x== 0 # also: or y == 1 or y == -1
461 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
463 # TODO: list context, upgrade
469 # local $Math::BigInt::accuracy = undef;
470 # local $Math::BigInt::precision = undef;
471 $x->{_n}->bmul($y->{_d});
472 $x->{_d}->bmul($y->{_n});
475 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
477 $x->bnorm()->round(@r);
481 ##############################################################################
486 # decrement value (subtract 1)
487 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
489 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
491 if ($x->{sign} eq '-')
493 $x->{_n}->badd($x->{_d}); # -5/2 => -7/2
497 if ($x->{_n}->bacmp($x->{_d}) < 0)
500 $x->{_n} = $x->{_d} - $x->{_n};
505 $x->{_n}->bsub($x->{_d}); # 5/2 => 3/2
508 $x->bnorm()->round(@r);
513 # increment value (add 1)
514 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
516 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
518 if ($x->{sign} eq '-')
520 if ($x->{_n}->bacmp($x->{_d}) < 0)
522 # -1/3 ++ => 2/3 (overflow at 0)
523 $x->{_n} = $x->{_d} - $x->{_n};
528 $x->{_n}->bsub($x->{_d}); # -5/2 => -3/2
533 $x->{_n}->badd($x->{_d}); # 5/2 => 7/2
535 $x->bnorm()->round(@r);
538 ##############################################################################
539 # is_foo methods (the rest is inherited)
543 # return true if arg (BRAT or num_str) is an integer
544 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
546 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
547 $x->{_d}->is_one(); # x/y && y != 1 => no integer
553 # return true if arg (BRAT or num_str) is zero
554 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
556 return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero();
562 # return true if arg (BRAT or num_str) is +1 or -1 if signis given
563 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
565 my $sign = shift || ''; $sign = '+' if $sign ne '-';
567 if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one());
573 # return true if arg (BFLOAT or num_str) is odd or false if even
574 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
576 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
577 ($x->{_d}->is_one() && $x->{_n}->is_odd()); # x/2 is not, but 3/1
583 # return true if arg (BINT or num_str) is even or false if odd
584 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
586 return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
587 return 1 if ($x->{_d}->is_one() # x/3 is never
588 && $x->{_n}->is_even()); # but 4/1 is
594 *objectify = \&Math::BigInt::objectify;
597 ##############################################################################
598 # parts() and friends
602 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
604 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
606 my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign};
612 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
614 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
620 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
622 return ($self->bnan(),$self->bnan()) if $x->{sign} eq 'NaN';
623 return ($self->binf(),$self->binf()) if $x->{sign} eq '+inf';
624 return ($self->binf('-'),$self->binf()) if $x->{sign} eq '-inf';
626 my $n = $x->{_n}->copy();
627 $n->{sign} = $x->{sign};
628 return ($n,$x->{_d}->copy());
641 ##############################################################################
642 # special calc routines
646 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
648 return $x unless $x->{sign} =~ /^[+-]$/;
649 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
651 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
653 $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1
654 $x->{sign} = '+' if $x->{_n}->is_zero(); # -0 => 0
660 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
662 return $x unless $x->{sign} =~ /^[+-]$/;
663 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
665 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
667 $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
673 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
675 if (($x->{sign} eq '+') && ($x->{_d}->is_one()))
678 return $x->round(@r);
688 my ($self,$x,$y,@r) = (ref($_[0]),@_);
689 # objectify is costly, so avoid it
690 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
692 ($self,$x,$y,@r) = objectify(2,@_);
695 return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
696 return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
697 return $x->bone(@r) if $y->is_zero();
698 return $x->round(@r) if $x->is_one() || $y->is_one();
699 if ($x->{sign} eq '-' && $x->{_n}->is_one() && $x->{_d}->is_one())
701 # if $x == -1 and odd/even y => +1/-1
702 return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
703 # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1;
705 # 1 ** -y => 1 / (1 ** |y|)
706 # so do test for negative $y after above's clause
707 # return $x->bnan() if $y->{sign} eq '-';
708 return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0)
710 # shortcut y/1 (and/or x/1)
711 if ($y->{_d}->is_one())
713 # shortcut for x/1 and y/1
714 if ($x->{_d}->is_one())
716 $x->{_n}->bpow($y->{_n}); # x/1 ** y/1 => (x ** y)/1
717 if ($y->{sign} eq '-')
719 # 0.2 ** -3 => 1/(0.2 ** 3)
720 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
722 # correct sign; + ** + => +
723 if ($x->{sign} eq '-')
725 # - * - => +, - * - * - => -
726 $x->{sign} = '+' if $y->{_n}->is_even();
728 return $x->round(@r);
731 $x->{_n}->bpow($y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y
732 $x->{_d}->bpow($y->{_n});
733 if ($y->{sign} eq '-')
735 # 0.2 ** -3 => 1/(0.2 ** 3)
736 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
738 # correct sign; + ** + => +
739 if ($x->{sign} eq '-')
741 # - * - => +, - * - * - => -
742 $x->{sign} = '+' if $y->{_n}->is_even();
744 return $x->round(@r);
747 # regular calculation (this is wrong for d/e ** f/g)
748 my $pow2 = $self->__one();
749 my $y1 = $MBI->new($y->{_n}/$y->{_d})->babs();
750 my $two = $MBI->new(2);
751 while (!$y1->is_one())
753 $pow2->bmul($x) if $y1->is_odd();
757 $x->bmul($pow2) unless $pow2->is_one();
758 # n ** -x => 1/n ** x
759 ($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-';
760 $x->bnorm()->round(@r);
765 return Math::BigRat->bnan();
770 my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
772 return $x->bnan() if $x->{sign} ne '+'; # inf, NaN, -1 etc
773 $x->{_d}->bsqrt($a,$p,$r);
774 $x->{_n}->bsqrt($a,$p,$r);
780 my ($self,$x,$y,$b,$a,$p,$r) = objectify(3,@_);
782 $x->bmul( $b->copy()->bpow($y), $a,$p,$r);
788 my ($self,$x,$y,$b,$a,$p,$r) = objectify(2,@_);
790 $x->bdiv( $b->copy()->bpow($y), $a,$p,$r);
794 ##############################################################################
812 ##############################################################################
817 my ($self,$x,$y) = objectify(2,@_);
819 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
821 # handle +-inf and NaN
822 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
823 return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/;
824 return +1 if $x->{sign} eq '+inf';
825 return -1 if $x->{sign} eq '-inf';
826 return -1 if $y->{sign} eq '+inf';
829 # check sign for speed first
830 return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y
831 return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0
834 my $xz = $x->{_n}->is_zero();
835 my $yz = $y->{_n}->is_zero();
836 return 0 if $xz && $yz; # 0 <=> 0
837 return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y
838 return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0
840 my $t = $x->{_n} * $y->{_d}; $t->{sign} = $x->{sign};
841 my $u = $y->{_n} * $x->{_d}; $u->{sign} = $y->{sign};
847 my ($self,$x,$y) = objectify(2,@_);
849 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
851 # handle +-inf and NaN
852 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
853 return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
854 return +1; # inf is always bigger
857 my $t = $x->{_n} * $y->{_d};
858 my $u = $y->{_n} * $x->{_d};
862 ##############################################################################
863 # output conversation
867 # convert 17/8 => float (aka 2.125)
868 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
870 return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, NaN, etc
872 my $t = Math::BigFloat->new($x->{_n});
873 $t->bneg() if $x->is_negative();
880 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
882 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc
883 my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3
884 $t->{sign} = $x->{sign};
893 for ( my $i = 0; $i < $l ; $i++)
895 # print "at $_[$i] (",$_[$i+1]||'undef',")\n";
896 if ( $_[$i] eq ':constant' )
898 # this rest causes overlord er load to step in
899 # print "overload @_\n";
900 overload::constant float => sub { $self->new(shift); };
902 # elsif ($_[$i] eq 'upgrade')
904 # # this causes upgrading
905 # $upgrade = $_[$i+1]; # or undef to disable
908 elsif ($_[$i] eq 'downgrade')
910 # this causes downgrading
911 $downgrade = $_[$i+1]; # or undef to disable
914 elsif ($_[$i] eq 'lib')
916 $lib = $_[$i+1] || ''; # default Calc
919 elsif ($_[$i] eq 'with')
921 $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt
929 # let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work
930 my $mbilib = eval { Math::BigInt->config()->{lib} };
931 if ((defined $mbilib) && ($MBI eq 'Math::BigInt'))
934 $MBI->import('lib',"$lib,$mbilib", 'objectify');
938 # MBI not loaded, or not with "Math::BigInt"
939 $lib .= ",$mbilib" if defined $mbilib;
943 # Perl < 5.6.0 dies with "out of memory!" when eval() and ':constant' is
944 # used in the same script, or eval inside import().
945 my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt
946 my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
947 $file = File::Spec->catfile (@parts, $file);
948 eval { require $file; $MBI->import( lib => '$lib', 'objectify' ); }
952 my $rc = "use $MBI lib => '$lib', 'objectify';";
956 die ("Couldn't load $MBI: $! $@") if $@;
958 # any non :constant stuff is handled by our parent, Exporter
959 # even if @_ is empty, to give it a chance
960 $self->SUPER::import(@a); # for subclasses
961 $self->export_to_level(1,$self,@a); # need this, too
970 Math::BigRat - arbitrarily big rationales
976 $x = Math::BigRat->new('3/7'); $x += '5/9';
978 print $x->bstr(),"\n";
983 Math::BigRat complements Math::BigInt and Math::BigFloat by providing support
984 for arbitrarily big rationales.
988 Math with the numbers is done (by default) by a module called
989 Math::BigInt::Calc. This is equivalent to saying:
991 use Math::BigRat lib => 'Calc';
993 You can change this by using:
995 use Math::BigRat lib => 'BitVect';
997 The following would first try to find Math::BigInt::Foo, then
998 Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc:
1000 use Math::BigRat lib => 'Foo,Math::BigInt::Bar';
1002 Calc.pm uses as internal format an array of elements of some decimal base
1003 (usually 1e7, but this might be different for some systems) with the least
1004 significant digit first, while BitVect.pm uses a bit vector of base 2, most
1005 significant bit first. Other modules might use even different means of
1006 representing the numbers. See the respective module documentation for further
1009 Currently the following replacement libraries exist, search for them at CPAN:
1011 Math::BigInt::BitVect
1014 Math::BigInt::FastCalc
1018 Any methods not listed here are dervied from Math::BigFloat (or
1019 Math::BigInt), so make sure you check these two modules for further
1024 $x = Math::BigRat->new('1/3');
1026 Create a new Math::BigRat object. Input can come in various forms:
1028 $x = Math::BigRat->new(123); # scalars
1029 $x = Math::BigRat->new('123.3'); # float
1030 $x = Math::BigRat->new('1/3'); # simple string
1031 $x = Math::BigRat->new('1 / 3'); # spaced
1032 $x = Math::BigRat->new('1 / 0.1'); # w/ floats
1033 $x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt
1034 $x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat
1035 $x = Math::BigRat->new(Math::BigInt::Lite->new('2')); # BigLite
1039 $n = $x->numerator();
1041 Returns a copy of the numerator (the part above the line) as signed BigInt.
1043 =head2 denominator()
1045 $d = $x->denominator();
1047 Returns a copy of the denominator (the part under the line) as positive BigInt.
1051 ($n,$d) = $x->parts();
1053 Return a list consisting of (signed) numerator and (unsigned) denominator as
1058 $x = Math::BigRat->new('13/7');
1059 print $x->as_number(),"\n"; # '1'
1061 Returns a copy of the object as BigInt by truncating it to integer.
1067 Calculates the factorial of $x. For instance:
1069 print Math::BigRat->new('3/1')->bfac(),"\n"; # 1*2*3
1070 print Math::BigRat->new('5/1')->bfac(),"\n"; # 1*2*3*4*5
1072 Works currently only for integers.
1076 Is not yet implemented.
1078 =head2 bround()/round()/bfround()
1080 Are not yet implemented.
1084 print "$x is 1\n" if $x->is_one();
1086 Return true if $x is exactly one, otherwise false.
1090 print "$x is 0\n" if $x->is_zero();
1092 Return true if $x is exactly zero, otherwise false.
1094 =head2 is_positive()
1096 print "$x is >= 0\n" if $x->is_positive();
1098 Return true if $x is positive (greater than or equal to zero), otherwise
1099 false. Please note that '+inf' is also positive, while 'NaN' and '-inf' aren't.
1101 =head2 is_negative()
1103 print "$x is < 0\n" if $x->is_negative();
1105 Return true if $x is negative (smaller than zero), otherwise false. Please
1106 note that '-inf' is also negative, while 'NaN' and '+inf' aren't.
1110 print "$x is an integer\n" if $x->is_int();
1112 Return true if $x has a denominator of 1 (e.g. no fraction parts), otherwise
1113 false. Please note that '-inf', 'inf' and 'NaN' aren't integer.
1117 print "$x is odd\n" if $x->is_odd();
1119 Return true if $x is odd, otherwise false.
1123 print "$x is even\n" if $x->is_even();
1125 Return true if $x is even, otherwise false.
1131 Set $x to the next bigger integer value (e.g. truncate the number to integer
1132 and then increment it by one).
1138 Truncate $x to an integer value.
1142 Some things are not yet implemented, or only implemented half-way:
1146 =item inf handling (partial)
1148 =item NaN handling (partial)
1150 =item rounding (not implemented except for bceil/bfloor)
1152 =item $x ** $y where $y is not an integer
1158 This program is free software; you may redistribute it and/or modify it under
1159 the same terms as Perl itself.
1163 L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>,
1164 L<Math::BigInt::Pari> and L<Math::BigInt::GMP>.
1166 See L<http://search.cpan.org/search?dist=bignum> for a way to use
1169 The package at L<http://search.cpan.org/search?dist=Math%3A%3ABigRat>
1170 may contain more documentation and examples as well as testcases.
1174 (C) by Tels L<http://bloodgate.com/> 2001-2002.