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 rational as untouchable
13 # You should not look at the innards of a BigRat - use the methods for this.
22 use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK $upgrade $downgrade
23 $accuracy $precision $round_mode $div_scale $_trap_nan $_trap_inf);
25 @ISA = qw(Exporter Math::BigFloat);
30 use overload; # inherit from Math::BigFloat
32 ##############################################################################
33 # global constants, flags and accessory
35 $accuracy = $precision = undef;
41 # these are internally, and not to be used from the outside
43 use constant MB_NEVER_ROUND => 0x0001;
45 $_trap_nan = 0; # are NaNs ok? set w/ config()
46 $_trap_inf = 0; # are infs ok? set w/ config()
49 my $class = 'Math::BigRat';
50 my $MBI = 'Math::BigInt';
54 return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't
60 # turn a single float input into a rational number (like '0.1')
63 return $self->bnan() if $f->is_nan();
64 return $self->binf('-inf') if $f->{sign} eq '-inf';
65 return $self->binf('+inf') if $f->{sign} eq '+inf';
67 $self->{_n} = $f->{_m}->copy(); # mantissa
68 $self->{_d} = $MBI->bone();
69 $self->{sign} = $f->{sign} || '+'; $self->{_n}->{sign} = '+';
70 if ($f->{_e}->{sign} eq '-')
72 # something like Math::BigRat->new('0.1');
73 $self->{_d}->blsft($f->{_e}->copy()->babs(),10); # 1 / 1 => 1/10
77 # something like Math::BigRat->new('10');
79 $self->{_n}->blsft($f->{_e},10) unless $f->{_e}->is_zero();
86 # create a Math::BigRat
91 my $self = { }; bless $self,$class;
93 # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet
95 if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat')))
97 if ($n->isa('Math::BigFloat'))
99 $self->_new_from_float($n);
101 if ($n->isa('Math::BigInt'))
103 # TODO: trap NaN, inf
104 $self->{_n} = $n->copy(); # "mantissa" = $n
105 $self->{_d} = $MBI->bone();
106 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
108 if ($n->isa('Math::BigInt::Lite'))
110 # TODO: trap NaN, inf
111 $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0;
112 $self->{_n} = $MBI->new(abs($$n),undef,undef); # "mantissa" = $n
113 $self->{_d} = $MBI->bone();
115 return $self->bnorm();
117 return $n->copy() if ref $n;
121 $self->{_n} = $MBI->bzero(); # undef => 0
122 $self->{_d} = $MBI->bone();
124 return $self->bnorm();
126 # string input with / delimiter
127 if ($n =~ /\s*\/\s*/)
129 return $class->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid
130 return $class->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid
131 ($n,$d) = split (/\//,$n);
132 # try as BigFloats first
133 if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/))
135 # one of them looks like a float
136 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so:
137 local $Math::BigFloat::accuracy = undef;
138 local $Math::BigFloat::precision = undef;
139 local $Math::BigInt::accuracy = undef;
140 local $Math::BigInt::precision = undef;
141 my $nf = Math::BigFloat->new($n);
143 return $self->bnan() if $nf->is_nan();
144 $self->{_n} = $nf->{_m};
145 # now correct $self->{_n} due to $n
146 my $f = Math::BigFloat->new($d,undef,undef);
147 $self->{_d} = $f->{_m};
148 return $self->bnan() if $f->is_nan();
149 #print "n=$nf e$nf->{_e} d=$f e$f->{_e}\n";
150 # calculate the difference between nE and dE
151 my $diff_e = $nf->{_e}->copy()->bsub ( $f->{_e} );
152 if ($diff_e->is_negative())
155 $self->{_d}->blsft($diff_e->babs(),10);
157 elsif (!$diff_e->is_zero())
160 $self->{_n}->blsft($diff_e,10);
165 # both d and n are (big)ints
166 $self->{_n} = $MBI->new($n,undef,undef);
167 $self->{_d} = $MBI->new($d,undef,undef);
169 return $self->bnan() if $self->{_n}->{sign} eq $nan ||
170 $self->{_d}->{sign} eq $nan;
171 # handle inf and NAN cases:
172 if ($self->{_n}->is_inf() || $self->{_d}->is_inf())
175 return $self->bnan() if
176 ($self->{_n}->is_inf() && $self->{_d}->is_inf());
177 if ($self->{_n}->is_inf())
179 my $s = '+'; # '+inf/+123' or '-inf/-123'
180 $s = '-' if substr($self->{_n}->{sign},0,1) ne $self->{_d}->{sign};
182 return $self->binf($s);
185 return $self->bzero();
188 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->babs();
189 # if $d is negative, flip sign
190 $self->{sign} =~ tr/+-/-+/ if $self->{_d}->{sign} eq '-';
191 $self->{_d}->babs(); # normalize
194 return $self->bnorm();
197 # simple string input
198 if (($n =~ /[\.eE]/))
200 # looks like a float, quacks like a float, so probably is a float
201 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so:
202 local $Math::BigFloat::accuracy = undef;
203 local $Math::BigFloat::precision = undef;
204 local $Math::BigInt::accuracy = undef;
205 local $Math::BigInt::precision = undef;
206 $self->{sign} = 'NaN';
207 $self->_new_from_float(Math::BigFloat->new($n,undef,undef));
211 $self->{_n} = $MBI->new($n,undef,undef);
212 $self->{_d} = $MBI->bone();
213 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->babs();
214 return $self->bnan() if $self->{sign} eq 'NaN';
215 return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/;
220 ##############################################################################
224 # return (later set?) configuration data as hash ref
225 my $class = shift || 'Math::BigFloat';
227 my $cfg = $class->SUPER::config(@_);
229 # now we need only to override the ones that are different from our parent
230 $cfg->{class} = $class;
235 ##############################################################################
239 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
241 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
243 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
247 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2'
249 return $s . $x->{_n}->bstr() if $x->{_d}->is_one();
250 $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
255 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
257 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
259 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
263 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
264 $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
269 # reduce the number to the shortest form and remember this (so that we
270 # don't reduce again)
271 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
273 # both parts must be BigInt's (or whatever we are using today)
274 if (ref($x->{_n}) ne $MBI)
276 require Carp; Carp::croak ("n is not $MBI but (".ref($x->{_n}).')');
278 if (ref($x->{_d}) ne $MBI)
280 require Carp; Carp::croak ("d is not $MBI but (".ref($x->{_d}).')');
283 # this is to prevent automatically rounding when MBI's globals are set
284 $x->{_d}->{_f} = MB_NEVER_ROUND;
285 $x->{_n}->{_f} = MB_NEVER_ROUND;
286 # 'forget' that parts were rounded via MBI::bround() in MBF's bfround()
287 delete $x->{_d}->{_a}; delete $x->{_n}->{_a};
288 delete $x->{_d}->{_p}; delete $x->{_n}->{_p};
290 # no normalize for NaN, inf etc.
291 return $x if $x->{sign} !~ /^[+-]$/;
293 # normalize zeros to 0/1
294 if (($x->{sign} =~ /^[+-]$/) &&
295 ($x->{_n}->is_zero()))
297 $x->{sign} = '+'; # never -0
298 $x->{_d} = $MBI->bone() unless $x->{_d}->is_one();
302 return $x if $x->{_d}->is_one(); # no need to reduce
304 # reduce other numbers
305 # disable upgrade in BigInt, otherwise deep recursion
306 local $Math::BigInt::upgrade = undef;
307 local $Math::BigInt::accuracy = undef;
308 local $Math::BigInt::precision = undef;
309 my $gcd = $x->{_n}->bgcd($x->{_d});
313 $x->{_n}->bdiv($gcd);
314 $x->{_d}->bdiv($gcd);
319 ##############################################################################
324 # used by parent class bnan() to initialize number to NaN
330 my $class = ref($self);
331 Carp::croak ("Tried to set $self to NaN in $class\::_bnan()");
333 $self->{_n} = $MBI->bzero();
334 $self->{_d} = $MBI->bzero();
339 # used by parent class bone() to initialize number to +inf/-inf
345 my $class = ref($self);
346 Carp::croak ("Tried to set $self to inf in $class\::_binf()");
348 $self->{_n} = $MBI->bzero();
349 $self->{_d} = $MBI->bzero();
354 # used by parent class bone() to initialize number to +1/-1
356 $self->{_n} = $MBI->bone();
357 $self->{_d} = $MBI->bone();
362 # used by parent class bzero() to initialize number to 0
364 $self->{_n} = $MBI->bzero();
365 $self->{_d} = $MBI->bone();
368 ##############################################################################
373 # add two rational numbers
376 my ($self,$x,$y,@r) = (ref($_[0]),@_);
377 # objectify is costly, so avoid it
378 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
380 ($self,$x,$y,@r) = objectify(2,@_);
383 $x = $self->new($x) unless $x->isa($self);
384 $y = $self->new($y) unless $y->isa($self);
386 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
389 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
390 # - + - = --------- = --
393 # we do not compute the gcd() here, but simple do:
395 # - + - = --------- = --
398 # the gcd() calculation and reducing is then done in bnorm()
400 local $Math::BigInt::accuracy = undef;
401 local $Math::BigInt::precision = undef;
403 $x->{_n}->bmul($y->{_d}); $x->{_n}->{sign} = $x->{sign};
404 my $m = $y->{_n}->copy()->bmul($x->{_d});
405 $m->{sign} = $y->{sign}; # 2/1 - 2/1
408 $x->{_d}->bmul($y->{_d});
410 # calculate sign of result and norm our _n part
411 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
413 $x->bnorm()->round(@r);
418 # subtract two rational numbers
421 my ($self,$x,$y,@r) = (ref($_[0]),@_);
422 # objectify is costly, so avoid it
423 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
425 ($self,$x,$y,@r) = objectify(2,@_);
428 # flip sign of $x, call badd(), then flip sign of result
429 $x->{sign} =~ tr/+-/-+/
430 unless $x->{sign} eq '+' && $x->{_n}->is_zero(); # not -0
431 $x->badd($y,@r); # does norm and round
432 $x->{sign} =~ tr/+-/-+/
433 unless $x->{sign} eq '+' && $x->{_n}->is_zero(); # not -0
439 # multiply two rational numbers
442 my ($self,$x,$y,@r) = (ref($_[0]),@_);
443 # objectify is costly, so avoid it
444 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
446 ($self,$x,$y,@r) = objectify(2,@_);
449 # TODO: $self instead or $class??
450 $x = $class->new($x) unless $x->isa($class);
451 $y = $class->new($y) unless $y->isa($class);
453 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
456 if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
458 return $x->bnan() if $x->is_zero() || $y->is_zero();
459 # result will always be +-inf:
460 # +inf * +/+inf => +inf, -inf * -/-inf => +inf
461 # +inf * -/-inf => -inf, -inf * +/+inf => -inf
462 return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/);
463 return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
464 return $x->binf('-');
467 # x== 0 # also: or y == 1 or y == -1
468 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
470 # According to Knuth, this can be optimized by doingtwice gcd (for d and n)
471 # and reducing in one step)
477 local $Math::BigInt::accuracy = undef;
478 local $Math::BigInt::precision = undef;
479 $x->{_n}->bmul($y->{_n});
480 $x->{_d}->bmul($y->{_d});
483 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
485 $x->bnorm()->round(@r);
490 # (dividend: BRAT or num_str, divisor: BRAT or num_str) return
491 # (BRAT,BRAT) (quo,rem) or BRAT (only rem)
494 my ($self,$x,$y,@r) = (ref($_[0]),@_);
495 # objectify is costly, so avoid it
496 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
498 ($self,$x,$y,@r) = objectify(2,@_);
501 # TODO: $self instead or $class??
502 $x = $class->new($x) unless $x->isa($class);
503 $y = $class->new($y) unless $y->isa($class);
505 return $self->_div_inf($x,$y)
506 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
508 # x== 0 # also: or y == 1 or y == -1
509 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
511 # TODO: list context, upgrade
517 # local $Math::BigInt::accuracy = undef;
518 # local $Math::BigInt::precision = undef;
519 $x->{_n}->bmul($y->{_d});
520 $x->{_d}->bmul($y->{_n});
523 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
525 $x->bnorm()->round(@r);
531 # compute "remainder" (in Perl way) of $x / $y
534 my ($self,$x,$y,@r) = (ref($_[0]),@_);
535 # objectify is costly, so avoid it
536 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
538 ($self,$x,$y,@r) = objectify(2,@_);
541 # TODO: $self instead or $class??
542 $x = $class->new($x) unless $x->isa($class);
543 $y = $class->new($y) unless $y->isa($class);
545 return $self->_div_inf($x,$y)
546 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
548 return $self->_div_inf($x,$y)
549 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
551 return $x if $x->is_zero(); # 0 / 7 = 0, mod 0
553 # compute $x - $y * floor($x/$y), keeping the sign of $x
555 # locally disable these, since they would interfere
556 local $Math::BigInt::upgrade = undef;
557 local $Math::BigInt::accuracy = undef;
558 local $Math::BigInt::precision = undef;
560 my $u = $x->copy()->babs();
561 # first, do a "normal" division ($x/$y)
562 $u->{_d}->bmul($y->{_n});
563 $u->{_n}->bmul($y->{_d});
566 if (!$u->{_d}->is_one())
568 $u->{_n}->bdiv($u->{_d}); # 22/7 => 3/1 w/ truncate
569 # no need to set $u->{_d} to 1, since later we set it to $y->{_d}
570 #$x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
574 $u->{_d} = $y->{_d}; # 1 * $y->{_d}, see floor above
575 $u->{_n}->bmul($y->{_n});
577 my $xsign = $x->{sign}; $x->{sign} = '+'; # remember sign and make abs
580 $x->{sign} = $xsign; # put sign back
582 $x->bnorm()->round(@r);
585 ##############################################################################
590 # decrement value (subtract 1)
591 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
593 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
595 if ($x->{sign} eq '-')
597 $x->{_n}->badd($x->{_d}); # -5/2 => -7/2
601 if ($x->{_n}->bacmp($x->{_d}) < 0)
604 $x->{_n} = $x->{_d} - $x->{_n};
609 $x->{_n}->bsub($x->{_d}); # 5/2 => 3/2
612 $x->bnorm()->round(@r);
617 # increment value (add 1)
618 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
620 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
622 if ($x->{sign} eq '-')
624 if ($x->{_n}->bacmp($x->{_d}) < 0)
626 # -1/3 ++ => 2/3 (overflow at 0)
627 $x->{_n} = $x->{_d} - $x->{_n};
632 $x->{_n}->bsub($x->{_d}); # -5/2 => -3/2
637 $x->{_n}->badd($x->{_d}); # 5/2 => 7/2
639 $x->bnorm()->round(@r);
642 ##############################################################################
643 # is_foo methods (the rest is inherited)
647 # return true if arg (BRAT or num_str) is an integer
648 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
650 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
651 $x->{_d}->is_one(); # x/y && y != 1 => no integer
657 # return true if arg (BRAT or num_str) is zero
658 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
660 return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero();
666 # return true if arg (BRAT or num_str) is +1 or -1 if signis given
667 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
669 my $sign = shift || ''; $sign = '+' if $sign ne '-';
671 if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one());
677 # return true if arg (BFLOAT or num_str) is odd or false if even
678 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
680 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
681 ($x->{_d}->is_one() && $x->{_n}->is_odd()); # x/2 is not, but 3/1
687 # return true if arg (BINT or num_str) is even or false if odd
688 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
690 return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
691 return 1 if ($x->{_d}->is_one() # x/3 is never
692 && $x->{_n}->is_even()); # but 4/1 is
698 *objectify = \&Math::BigInt::objectify;
701 ##############################################################################
702 # parts() and friends
706 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
708 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
710 my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign};
716 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
718 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
724 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
726 return ($self->bnan(),$self->bnan()) if $x->{sign} eq 'NaN';
727 return ($self->binf(),$self->binf()) if $x->{sign} eq '+inf';
728 return ($self->binf('-'),$self->binf()) if $x->{sign} eq '-inf';
730 my $n = $x->{_n}->copy();
731 $n->{sign} = $x->{sign};
732 return ($n,$x->{_d}->copy());
745 ##############################################################################
746 # special calc routines
750 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
752 return $x unless $x->{sign} =~ /^[+-]$/;
753 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
755 local $Math::BigInt::upgrade = undef;
756 local $Math::BigInt::accuracy = undef;
757 local $Math::BigInt::precision = undef;
758 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
760 $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1
761 $x->{sign} = '+' if $x->{_n}->is_zero(); # -0 => 0
767 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
769 return $x unless $x->{sign} =~ /^[+-]$/;
770 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
772 local $Math::BigInt::upgrade = undef;
773 local $Math::BigInt::accuracy = undef;
774 local $Math::BigInt::precision = undef;
775 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
777 $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
783 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
785 # if $x is an integer
786 if (($x->{sign} eq '+') && ($x->{_d}->is_one()))
789 return $x->round(@r);
799 my ($self,$x,$y,@r) = (ref($_[0]),@_);
800 # objectify is costly, so avoid it
801 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
803 ($self,$x,$y,@r) = objectify(2,@_);
806 return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
807 return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
808 return $x->bone(@r) if $y->is_zero();
809 return $x->round(@r) if $x->is_one() || $y->is_one();
810 if ($x->{sign} eq '-' && $x->{_n}->is_one() && $x->{_d}->is_one())
812 # if $x == -1 and odd/even y => +1/-1
813 return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
814 # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1;
816 # 1 ** -y => 1 / (1 ** |y|)
817 # so do test for negative $y after above's clause
818 # return $x->bnan() if $y->{sign} eq '-';
819 return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0)
821 # shortcut y/1 (and/or x/1)
822 if ($y->{_d}->is_one())
824 # shortcut for x/1 and y/1
825 if ($x->{_d}->is_one())
827 $x->{_n}->bpow($y->{_n}); # x/1 ** y/1 => (x ** y)/1
828 if ($y->{sign} eq '-')
830 # 0.2 ** -3 => 1/(0.2 ** 3)
831 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
833 # correct sign; + ** + => +
834 if ($x->{sign} eq '-')
836 # - * - => +, - * - * - => -
837 $x->{sign} = '+' if $y->{_n}->is_even();
839 return $x->round(@r);
842 $x->{_n}->bpow($y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y
843 $x->{_d}->bpow($y->{_n});
844 if ($y->{sign} eq '-')
846 # 0.2 ** -3 => 1/(0.2 ** 3)
847 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
849 # correct sign; + ** + => +
850 if ($x->{sign} eq '-')
852 # - * - => +, - * - * - => -
853 $x->{sign} = '+' if $y->{_n}->is_even();
855 return $x->round(@r);
858 # regular calculation (this is wrong for d/e ** f/g)
859 my $pow2 = $self->__one();
860 my $y1 = $MBI->new($y->{_n}/$y->{_d})->babs();
861 my $two = $MBI->new(2);
862 while (!$y1->is_one())
864 $pow2->bmul($x) if $y1->is_odd();
868 $x->bmul($pow2) unless $pow2->is_one();
869 # n ** -x => 1/n ** x
870 ($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-';
871 $x->bnorm()->round(@r);
877 my ($self,$x,$y,@r) = (ref($_[0]),@_);
879 # objectify is costly, so avoid it
880 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
882 ($self,$x,$y,@r) = objectify(2,@_);
886 return $x->bnan() if $x->is_zero() || $x->{sign} ne '+' || $y->{sign} ne '+';
888 if ($x->is_int() && $y->is_int())
890 return $self->new($x->as_number()->blog($y->as_number(),@r));
893 warn ("blog() not fully implemented");
900 my ($self,$x,$y,@r) = (ref($_[0]),@_);
901 # objectify is costly, so avoid it
902 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
904 ($self,$x,$y,@r) = objectify(2,@_);
907 if ($x->is_int() && $y->is_int())
909 return $self->new($x->as_number()->broot($y->as_number(),@r));
912 warn ("broot() not fully implemented");
919 my ($self,$x,$y,$m,@r) = (ref($_[0]),@_);
920 # objectify is costly, so avoid it
921 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
923 ($self,$x,$y,$m,@r) = objectify(3,@_);
926 # $x or $y or $m are NaN or +-inf => NaN
928 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/ ||
929 $m->{sign} !~ /^[+-]$/;
931 if ($x->is_int() && $y->is_int() && $m->is_int())
933 return $self->new($x->as_number()->bmodpow($y->as_number(),$m,@r));
936 warn ("bmodpow() not fully implemented");
943 my ($self,$x,$y,@r) = (ref($_[0]),@_);
944 # objectify is costly, so avoid it
945 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
947 ($self,$x,$y,@r) = objectify(2,@_);
950 # $x or $y are NaN or +-inf => NaN
952 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/;
954 if ($x->is_int() && $y->is_int())
956 return $self->new($x->as_number()->bmodinv($y->as_number(),@r));
959 warn ("bmodinv() not fully implemented");
965 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
967 return $x->bnan() if $x->{sign} !~ /^[+]/; # NaN, -inf or < 0
968 return $x if $x->{sign} eq '+inf'; # sqrt(inf) == inf
969 return $x->round(@r) if $x->is_zero() || $x->is_one();
971 local $Math::BigFloat::upgrade = undef;
972 local $Math::BigFloat::downgrade = undef;
973 local $Math::BigFloat::precision = undef;
974 local $Math::BigFloat::accuracy = undef;
975 local $Math::BigInt::upgrade = undef;
976 local $Math::BigInt::precision = undef;
977 local $Math::BigInt::accuracy = undef;
978 $x->{_d} = Math::BigFloat->new($x->{_d})->bsqrt();
979 $x->{_n} = Math::BigFloat->new($x->{_n})->bsqrt();
981 # if sqrt(D) was not integer
982 if ($x->{_d}->{_e}->{sign} ne '+')
984 $x->{_n}->blsft($x->{_d}->{_e}->babs(),10); # 7.1/4.51 => 7.1/45.1
985 $x->{_d} = $x->{_d}->{_m}; # 7.1/45.1 => 71/45.1
987 # if sqrt(N) was not integer
988 if ($x->{_n}->{_e}->{sign} ne '+')
990 $x->{_d}->blsft($x->{_n}->{_e}->babs(),10); # 71/45.1 => 710/45.1
991 $x->{_n} = $x->{_n}->{_m}; # 710/45.1 => 710/451
994 # convert parts to $MBI again
995 $x->{_n} = $x->{_n}->as_number();
996 $x->{_d} = $x->{_d}->as_number();
997 $x->bnorm()->round(@r);
1002 my ($self,$x,$y,$b,$a,$p,$r) = objectify(3,@_);
1004 $x->bmul( $b->copy()->bpow($y), $a,$p,$r);
1010 my ($self,$x,$y,$b,$a,$p,$r) = objectify(2,@_);
1012 $x->bdiv( $b->copy()->bpow($y), $a,$p,$r);
1016 ##############################################################################
1034 ##############################################################################
1039 # compare two signed numbers
1042 my ($self,$x,$y) = (ref($_[0]),@_);
1043 # objectify is costly, so avoid it
1044 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
1046 ($self,$x,$y) = objectify(2,@_);
1049 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
1051 # handle +-inf and NaN
1052 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
1053 return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/;
1054 return +1 if $x->{sign} eq '+inf';
1055 return -1 if $x->{sign} eq '-inf';
1056 return -1 if $y->{sign} eq '+inf';
1059 # check sign for speed first
1060 return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y
1061 return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0
1064 my $xz = $x->{_n}->is_zero();
1065 my $yz = $y->{_n}->is_zero();
1066 return 0 if $xz && $yz; # 0 <=> 0
1067 return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y
1068 return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0
1070 my $t = $x->{_n} * $y->{_d}; $t->{sign} = $x->{sign};
1071 my $u = $y->{_n} * $x->{_d}; $u->{sign} = $y->{sign};
1077 # compare two numbers (as unsigned)
1080 my ($self,$x,$y) = (ref($_[0]),@_);
1081 # objectify is costly, so avoid it
1082 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
1084 ($self,$x,$y) = objectify(2,@_);
1087 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
1089 # handle +-inf and NaN
1090 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
1091 return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
1092 return 1 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/;
1096 my $t = $x->{_n} * $y->{_d};
1097 my $u = $y->{_n} * $x->{_d};
1101 ##############################################################################
1102 # output conversation
1106 # convert 17/8 => float (aka 2.125)
1107 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
1109 return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, NaN, etc
1112 return $x->{_n}->numify() if $x->{_d}->is_one();
1115 my $neg = 1; $neg = -1 if $x->{sign} ne '+';
1116 $neg * $x->{_n}->numify() / $x->{_d}->numify(); # return sign * N/D
1121 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
1123 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc
1125 # need to disable these, otherwise bdiv() gives BigRat again
1126 local $Math::BigInt::upgrade = undef;
1127 local $Math::BigInt::accuracy = undef;
1128 local $Math::BigInt::precision = undef;
1129 my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3
1130 $t->{sign} = $x->{sign};
1138 my $lib = ''; my @a;
1139 for ( my $i = 0; $i < $l ; $i++)
1141 # print "at $_[$i] (",$_[$i+1]||'undef',")\n";
1142 if ( $_[$i] eq ':constant' )
1144 # this rest causes overlord er load to step in
1145 # print "overload @_\n";
1146 overload::constant float => sub { $self->new(shift); };
1148 # elsif ($_[$i] eq 'upgrade')
1150 # # this causes upgrading
1151 # $upgrade = $_[$i+1]; # or undef to disable
1154 elsif ($_[$i] eq 'downgrade')
1156 # this causes downgrading
1157 $downgrade = $_[$i+1]; # or undef to disable
1160 elsif ($_[$i] eq 'lib')
1162 $lib = $_[$i+1] || ''; # default Calc
1165 elsif ($_[$i] eq 'with')
1167 $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt
1175 # let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work
1176 my $mbilib = eval { Math::BigInt->config()->{lib} };
1177 if ((defined $mbilib) && ($MBI eq 'Math::BigInt'))
1179 # MBI already loaded
1180 $MBI->import('lib',"$lib,$mbilib", 'objectify');
1184 # MBI not loaded, or not with "Math::BigInt"
1185 $lib .= ",$mbilib" if defined $mbilib;
1189 # Perl < 5.6.0 dies with "out of memory!" when eval() and ':constant' is
1190 # used in the same script, or eval inside import().
1191 my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt
1192 my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
1193 $file = File::Spec->catfile (@parts, $file);
1194 eval { require $file; $MBI->import( lib => '$lib', 'objectify' ); }
1198 my $rc = "use $MBI lib => '$lib', 'objectify';";
1204 require Carp; Carp::croak ("Couldn't load $MBI: $! $@");
1207 # any non :constant stuff is handled by our parent, Exporter
1208 # even if @_ is empty, to give it a chance
1209 $self->SUPER::import(@a); # for subclasses
1210 $self->export_to_level(1,$self,@a); # need this, too
1219 Math::BigRat - arbitrarily big rational numbers
1225 my $x = Math::BigRat->new('3/7'); $x += '5/9';
1227 print $x->bstr(),"\n";
1230 my $y = Math::BigRat->new('inf');
1231 print "$y ", ($y->is_inf ? 'is' : 'is not') , " infinity\n";
1233 my $z = Math::BigRat->new(144); $z->bsqrt();
1237 Math::BigRat complements Math::BigInt and Math::BigFloat by providing support
1238 for arbitrarily big rational numbers.
1242 Math with the numbers is done (by default) by a module called
1243 Math::BigInt::Calc. This is equivalent to saying:
1245 use Math::BigRat lib => 'Calc';
1247 You can change this by using:
1249 use Math::BigRat lib => 'BitVect';
1251 The following would first try to find Math::BigInt::Foo, then
1252 Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc:
1254 use Math::BigRat lib => 'Foo,Math::BigInt::Bar';
1256 Calc.pm uses as internal format an array of elements of some decimal base
1257 (usually 1e7, but this might be different for some systems) with the least
1258 significant digit first, while BitVect.pm uses a bit vector of base 2, most
1259 significant bit first. Other modules might use even different means of
1260 representing the numbers. See the respective module documentation for further
1263 Currently the following replacement libraries exist, search for them at CPAN:
1265 Math::BigInt::BitVect
1268 Math::BigInt::FastCalc
1272 Any methods not listed here are dervied from Math::BigFloat (or
1273 Math::BigInt), so make sure you check these two modules for further
1278 $x = Math::BigRat->new('1/3');
1280 Create a new Math::BigRat object. Input can come in various forms:
1282 $x = Math::BigRat->new(123); # scalars
1283 $x = Math::BigRat->new('inf'); # infinity
1284 $x = Math::BigRat->new('123.3'); # float
1285 $x = Math::BigRat->new('1/3'); # simple string
1286 $x = Math::BigRat->new('1 / 3'); # spaced
1287 $x = Math::BigRat->new('1 / 0.1'); # w/ floats
1288 $x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt
1289 $x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat
1290 $x = Math::BigRat->new(Math::BigInt::Lite->new('2')); # BigLite
1294 $n = $x->numerator();
1296 Returns a copy of the numerator (the part above the line) as signed BigInt.
1298 =head2 denominator()
1300 $d = $x->denominator();
1302 Returns a copy of the denominator (the part under the line) as positive BigInt.
1306 ($n,$d) = $x->parts();
1308 Return a list consisting of (signed) numerator and (unsigned) denominator as
1313 $x = Math::BigRat->new('13/7');
1314 print $x->as_number(),"\n"; # '1'
1316 Returns a copy of the object as BigInt trunced it to integer.
1322 Calculates the factorial of $x. For instance:
1324 print Math::BigRat->new('3/1')->bfac(),"\n"; # 1*2*3
1325 print Math::BigRat->new('5/1')->bfac(),"\n"; # 1*2*3*4*5
1327 Works currently only for integers.
1331 Is not yet implemented.
1333 =head2 bround()/round()/bfround()
1335 Are not yet implemented.
1340 my $x = Math::BigRat->new('7/4');
1341 my $y = Math::BigRat->new('4/3');
1344 Set $x to the remainder of the division of $x by $y.
1348 print "$x is 1\n" if $x->is_one();
1350 Return true if $x is exactly one, otherwise false.
1354 print "$x is 0\n" if $x->is_zero();
1356 Return true if $x is exactly zero, otherwise false.
1358 =head2 is_positive()
1360 print "$x is >= 0\n" if $x->is_positive();
1362 Return true if $x is positive (greater than or equal to zero), otherwise
1363 false. Please note that '+inf' is also positive, while 'NaN' and '-inf' aren't.
1365 =head2 is_negative()
1367 print "$x is < 0\n" if $x->is_negative();
1369 Return true if $x is negative (smaller than zero), otherwise false. Please
1370 note that '-inf' is also negative, while 'NaN' and '+inf' aren't.
1374 print "$x is an integer\n" if $x->is_int();
1376 Return true if $x has a denominator of 1 (e.g. no fraction parts), otherwise
1377 false. Please note that '-inf', 'inf' and 'NaN' aren't integer.
1381 print "$x is odd\n" if $x->is_odd();
1383 Return true if $x is odd, otherwise false.
1387 print "$x is even\n" if $x->is_even();
1389 Return true if $x is even, otherwise false.
1395 Set $x to the next bigger integer value (e.g. truncate the number to integer
1396 and then increment it by one).
1402 Truncate $x to an integer value.
1408 Calculate the square root of $x.
1414 print Dumper ( Math::BigRat->config() );
1415 print Math::BigRat->config()->{lib},"\n";
1417 Returns a hash containing the configuration, e.g. the version number, lib
1418 loaded etc. The following hash keys are currently filled in with the
1419 appropriate information.
1421 key RO/RW Description
1423 ============================================================
1424 lib RO Name of the Math library
1426 lib_version RO Version of 'lib'
1428 class RO The class of config you just called
1430 version RO version number of the class you used
1432 upgrade RW To which class numbers are upgraded
1434 downgrade RW To which class numbers are downgraded
1436 precision RW Global precision
1438 accuracy RW Global accuracy
1440 round_mode RW Global round mode
1442 div_scale RW Fallback acccuracy for div
1444 trap_nan RW Trap creation of NaN (undef = no)
1446 trap_inf RW Trap creation of +inf/-inf (undef = no)
1449 By passing a reference to a hash you may set the configuration values. This
1450 works only for values that a marked with a C<RW> above, anything else is
1455 Some things are not yet implemented, or only implemented half-way:
1459 =item inf handling (partial)
1461 =item NaN handling (partial)
1463 =item rounding (not implemented except for bceil/bfloor)
1465 =item $x ** $y where $y is not an integer
1467 =item bmod(), blog(), bmodinv() and bmodpow() (partial)
1473 This program is free software; you may redistribute it and/or modify it under
1474 the same terms as Perl itself.
1478 L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>,
1479 L<Math::BigInt::Pari> and L<Math::BigInt::GMP>.
1481 See L<http://search.cpan.org/search?dist=bignum> for a way to use
1484 The package at L<http://search.cpan.org/search?dist=Math%3A%3ABigRat>
1485 may contain more documentation and examples as well as testcases.
1489 (C) by Tels L<http://bloodgate.com/> 2001, 2002, 2003, 2004.