1 2# 3# "Tax the rat farms." - Lord Vetinari 4# 5 6# The following hash values are used: 7# sign : +,-,NaN,+inf,-inf 8# _d : denominator 9# _n : numeraotr (value = _n/_d) 10# _a : accuracy 11# _p : precision 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. 14 15package Math::BigRat; 16 17require 5.005_03; 18use strict; 19 20require Exporter; 21use Math::BigFloat; 22use vars qw($VERSION @ISA $PACKAGE $upgrade $downgrade 23 $accuracy $precision $round_mode $div_scale $_trap_nan $_trap_inf); 24 25@ISA = qw(Exporter Math::BigFloat); 26 27$VERSION = '0.12'; 28 29use overload; # inherit from Math::BigFloat 30 31BEGIN { *objectify = \&Math::BigInt::objectify; } 32 33############################################################################## 34# global constants, flags and accessory 35 36$accuracy = $precision = undef; 37$round_mode = 'even'; 38$div_scale = 40; 39$upgrade = undef; 40$downgrade = undef; 41 42# these are internally, and not to be used from the outside 43 44use constant MB_NEVER_ROUND => 0x0001; 45 46$_trap_nan = 0; # are NaNs ok? set w/ config() 47$_trap_inf = 0; # are infs ok? set w/ config() 48 49my $nan = 'NaN'; 50my $MBI = 'Math::BigInt'; 51my $CALC = 'Math::BigInt::Calc'; 52my $class = 'Math::BigRat'; 53my $IMPORT = 0; 54 55sub isa 56 { 57 return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't 58 UNIVERSAL::isa(@_); 59 } 60 61sub BEGIN 62 { 63 *AUTOLOAD = \&Math::BigFloat::AUTOLOAD; 64 } 65 66sub _new_from_float 67 { 68 # turn a single float input into a rational number (like '0.1') 69 my ($self,$f) = @_; 70 71 return $self->bnan() if $f->is_nan(); 72 return $self->binf($f->{sign}) if $f->{sign} =~ /^[+-]inf$/; 73 74 local $Math::BigInt::accuracy = undef; 75 local $Math::BigInt::precision = undef; 76 $self->{_n} = $MBI->new($CALC->_str ( $f->{_m} ),undef,undef);# mantissa 77 $self->{_d} = $MBI->bone(); 78 $self->{sign} = $f->{sign} || '+'; 79 if ($f->{_es} eq '-') 80 { 81 # something like Math::BigRat->new('0.1'); 82 # 1 / 1 => 1/10 83 $self->{_d}->blsft( $MBI->new($CALC->_str ( $f->{_e} )),10); 84 } 85 else 86 { 87 # something like Math::BigRat->new('10'); 88 # 1 / 1 => 10/1 89 $self->{_n}->blsft( $MBI->new($CALC->_str($f->{_e})),10) unless 90 $CALC->_is_zero($f->{_e}); 91 } 92 $self; 93 } 94 95sub new 96 { 97 # create a Math::BigRat 98 my $class = shift; 99 100 my ($n,$d) = shift; 101 102 my $self = { }; bless $self,$class; 103 104 # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet 105 106 if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat'))) 107 { 108 if ($n->isa('Math::BigFloat')) 109 { 110 $self->_new_from_float($n); 111 } 112 if ($n->isa('Math::BigInt')) 113 { 114 # TODO: trap NaN, inf 115 $self->{_n} = $n->copy(); # "mantissa" = $n 116 $self->{_d} = $MBI->bone(); 117 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+'; 118 } 119 if ($n->isa('Math::BigInt::Lite')) 120 { 121 # TODO: trap NaN, inf 122 $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0; 123 $self->{_n} = $MBI->new(abs($$n),undef,undef); # "mantissa" = $n 124 $self->{_d} = $MBI->bone(); 125 } 126 return $self->bnorm(); 127 } 128 return $n->copy() if ref $n; 129 130 if (!defined $n) 131 { 132 $self->{_n} = $MBI->bzero(); # undef => 0 133 $self->{_d} = $MBI->bone(); 134 $self->{sign} = '+'; 135 return $self->bnorm(); 136 } 137 # string input with / delimiter 138 if ($n =~ /\s*\/\s*/) 139 { 140 return $class->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid 141 return $class->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid 142 ($n,$d) = split (/\//,$n); 143 # try as BigFloats first 144 if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/)) 145 { 146 # one of them looks like a float 147 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so: 148 local $Math::BigFloat::accuracy = undef; 149 local $Math::BigFloat::precision = undef; 150 local $Math::BigInt::accuracy = undef; 151 local $Math::BigInt::precision = undef; 152 153 my $nf = Math::BigFloat->new($n,undef,undef); 154 $self->{sign} = '+'; 155 return $self->bnan() if $nf->is_nan(); 156 $self->{_n} = $MBI->new( $CALC->_str( $nf->{_m} ) ); 157 158 # now correct $self->{_n} due to $n 159 my $f = Math::BigFloat->new($d,undef,undef); 160 return $self->bnan() if $f->is_nan(); 161 $self->{_d} = $MBI->new( $CALC->_str( $f->{_m} ) ); 162 163 # calculate the difference between nE and dE 164 my $diff_e = $MBI->new ($nf->exponent())->bsub ( $f->exponent); 165 if ($diff_e->is_negative()) 166 { 167 # < 0: mul d with it 168 $self->{_d}->blsft($diff_e->babs(),10); 169 } 170 elsif (!$diff_e->is_zero()) 171 { 172 # > 0: mul n with it 173 $self->{_n}->blsft($diff_e,10); 174 } 175 } 176 else 177 { 178 # both d and n are (big)ints 179 $self->{_n} = $MBI->new($n,undef,undef); 180 $self->{_d} = $MBI->new($d,undef,undef); 181 $self->{sign} = '+'; 182 return $self->bnan() if $self->{_n}->{sign} eq $nan || 183 $self->{_d}->{sign} eq $nan; 184 # handle inf and NAN cases: 185 if ($self->{_n}->is_inf() || $self->{_d}->is_inf()) 186 { 187 # inf/inf => NaN 188 return $self->bnan() if 189 ($self->{_n}->is_inf() && $self->{_d}->is_inf()); 190 if ($self->{_n}->is_inf()) 191 { 192 my $s = '+'; # '+inf/+123' or '-inf/-123' 193 $s = '-' if substr($self->{_n}->{sign},0,1) ne $self->{_d}->{sign}; 194 # +-inf/123 => +-inf 195 return $self->binf($s); 196 } 197 # 123/inf => 0 198 return $self->bzero(); 199 } 200 201 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->babs(); 202 # if $d is negative, flip sign 203 $self->{sign} =~ tr/+-/-+/ if $self->{_d}->{sign} eq '-'; 204 $self->{_d}->babs(); # normalize 205 } 206 207 return $self->bnorm(); 208 } 209 210 # simple string input 211 if (($n =~ /[\.eE]/)) 212 { 213 # looks like a float, quacks like a float, so probably is a float 214 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so: 215 local $Math::BigFloat::accuracy = undef; 216 local $Math::BigFloat::precision = undef; 217 local $Math::BigInt::accuracy = undef; 218 local $Math::BigInt::precision = undef; 219 $self->{sign} = 'NaN'; 220 $self->_new_from_float(Math::BigFloat->new($n,undef,undef)); 221 } 222 else 223 { 224 $self->{_n} = $MBI->new($n,undef,undef); 225 $self->{_d} = $MBI->bone(); 226 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->babs(); 227 return $self->bnan() if $self->{sign} eq 'NaN'; 228 return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/; 229 } 230 $self->bnorm(); 231 } 232 233sub copy 234 { 235 my ($c,$x); 236 if (@_ > 1) 237 { 238 # if two arguments, the first one is the class to "swallow" subclasses 239 ($c,$x) = @_; 240 } 241 else 242 { 243 $x = shift; 244 $c = ref($x); 245 } 246 return unless ref($x); # only for objects 247 248 my $self = {}; bless $self,$c; 249 250 $self->{sign} = $x->{sign}; 251 $self->{_d} = $x->{_d}->copy(); 252 $self->{_n} = $x->{_n}->copy(); 253 $self->{_a} = $x->{_a} if defined $x->{_a}; 254 $self->{_p} = $x->{_p} if defined $x->{_p}; 255 $self; 256 } 257 258############################################################################## 259 260sub config 261 { 262 # return (later set?) configuration data as hash ref 263 my $class = shift || 'Math::BigFloat'; 264 265 my $cfg = $class->SUPER::config(@_); 266 267 # now we need only to override the ones that are different from our parent 268 $cfg->{class} = $class; 269 $cfg->{with} = $MBI; 270 $cfg; 271 } 272 273############################################################################## 274 275sub bstr 276 { 277 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 278 279 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc 280 { 281 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf 282 return $s; 283 } 284 285 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2' 286 287 return $s . $x->{_n}->bstr() if $x->{_d}->is_one(); 288 $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr(); 289 } 290 291sub bsstr 292 { 293 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 294 295 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc 296 { 297 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf 298 return $s; 299 } 300 301 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3 302 $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr(); 303 } 304 305sub bnorm 306 { 307 # reduce the number to the shortest form and remember this (so that we 308 # don't reduce again) 309 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 310 311 # both parts must be BigInt's (or whatever we are using today) 312 if (ref($x->{_n}) ne $MBI) 313 { 314 require Carp; Carp::croak ("n is not $MBI but (".ref($x->{_n}).')'); 315 } 316 if (ref($x->{_d}) ne $MBI) 317 { 318 require Carp; Carp::croak ("d is not $MBI but (".ref($x->{_d}).')'); 319 } 320 321 # this is to prevent automatically rounding when MBI's globals are set 322 $x->{_d}->{_f} = MB_NEVER_ROUND; 323 $x->{_n}->{_f} = MB_NEVER_ROUND; 324 # 'forget' that parts were rounded via MBI::bround() in MBF's bfround() 325 delete $x->{_d}->{_a}; delete $x->{_n}->{_a}; 326 delete $x->{_d}->{_p}; delete $x->{_n}->{_p}; 327 328 # no normalize for NaN, inf etc. 329 return $x if $x->{sign} !~ /^[+-]$/; 330 331 # normalize zeros to 0/1 332 if (($x->{sign} =~ /^[+-]$/) && 333 ($x->{_n}->is_zero())) 334 { 335 $x->{sign} = '+'; # never -0 336 $x->{_d} = $MBI->bone() unless $x->{_d}->is_one(); 337 return $x; 338 } 339 340 return $x if $x->{_d}->is_one(); # no need to reduce 341 342 # reduce other numbers 343 # disable upgrade in BigInt, otherwise deep recursion 344 local $Math::BigInt::upgrade = undef; 345 local $Math::BigInt::accuracy = undef; 346 local $Math::BigInt::precision = undef; 347 my $gcd = $x->{_n}->bgcd($x->{_d}); 348 349 if (!$gcd->is_one()) 350 { 351 $x->{_n}->bdiv($gcd); 352 $x->{_d}->bdiv($gcd); 353 } 354 $x; 355 } 356 357############################################################################## 358# special values 359 360sub _bnan 361 { 362 # used by parent class bnan() to initialize number to NaN 363 my $self = shift; 364 365 if ($_trap_nan) 366 { 367 require Carp; 368 my $class = ref($self); 369 Carp::croak ("Tried to set $self to NaN in $class\::_bnan()"); 370 } 371 $self->{_n} = $MBI->bzero(); 372 $self->{_d} = $MBI->bzero(); 373 } 374 375sub _binf 376 { 377 # used by parent class bone() to initialize number to +inf/-inf 378 my $self = shift; 379 380 if ($_trap_inf) 381 { 382 require Carp; 383 my $class = ref($self); 384 Carp::croak ("Tried to set $self to inf in $class\::_binf()"); 385 } 386 $self->{_n} = $MBI->bzero(); 387 $self->{_d} = $MBI->bzero(); 388 } 389 390sub _bone 391 { 392 # used by parent class bone() to initialize number to +1/-1 393 my $self = shift; 394 $self->{_n} = $MBI->bone(); 395 $self->{_d} = $MBI->bone(); 396 } 397 398sub _bzero 399 { 400 # used by parent class bzero() to initialize number to 0 401 my $self = shift; 402 $self->{_n} = $MBI->bzero(); 403 $self->{_d} = $MBI->bone(); 404 } 405 406############################################################################## 407# mul/add/div etc 408 409sub badd 410 { 411 # add two rational numbers 412 413 # set up parameters 414 my ($self,$x,$y,@r) = (ref($_[0]),@_); 415 # objectify is costly, so avoid it 416 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 417 { 418 ($self,$x,$y,@r) = objectify(2,@_); 419 } 420 421 $x = $self->new($x) unless $x->isa($self); 422 $y = $self->new($y) unless $y->isa($self); 423 424 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN'); 425 # TODO: inf handling 426 427 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7 428 # - + - = --------- = -- 429 # 4 3 4*3 12 430 431 # we do not compute the gcd() here, but simple do: 432 # 5 7 5*3 + 7*4 41 433 # - + - = --------- = -- 434 # 4 3 4*3 12 435 436 # the gcd() calculation and reducing is then done in bnorm() 437 438 local $Math::BigInt::accuracy = undef; 439 local $Math::BigInt::precision = undef; 440 441 $x->{_n}->bmul($y->{_d}); $x->{_n}->{sign} = $x->{sign}; 442 my $m = $y->{_n}->copy()->bmul($x->{_d}); 443 $m->{sign} = $y->{sign}; # 2/1 - 2/1 444 $x->{_n}->badd($m); 445 446 $x->{_d}->bmul($y->{_d}); 447 448 # calculate sign of result and norm our _n part 449 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+'; 450 451 $x->bnorm()->round(@r); 452 } 453 454sub bsub 455 { 456 # subtract two rational numbers 457 458 # set up parameters 459 my ($self,$x,$y,@r) = (ref($_[0]),@_); 460 # objectify is costly, so avoid it 461 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 462 { 463 ($self,$x,$y,@r) = objectify(2,@_); 464 } 465 466 # flip sign of $x, call badd(), then flip sign of result 467 $x->{sign} =~ tr/+-/-+/ 468 unless $x->{sign} eq '+' && $x->{_n}->is_zero(); # not -0 469 $x->badd($y,@r); # does norm and round 470 $x->{sign} =~ tr/+-/-+/ 471 unless $x->{sign} eq '+' && $x->{_n}->is_zero(); # not -0 472 $x; 473 } 474 475sub bmul 476 { 477 # multiply two rational numbers 478 479 # set up parameters 480 my ($self,$x,$y,@r) = (ref($_[0]),@_); 481 # objectify is costly, so avoid it 482 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 483 { 484 ($self,$x,$y,@r) = objectify(2,@_); 485 } 486 487 $x = $self->new($x) unless $x->isa($self); 488 $y = $self->new($y) unless $y->isa($self); 489 490 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN'); 491 492 # inf handling 493 if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/)) 494 { 495 return $x->bnan() if $x->is_zero() || $y->is_zero(); 496 # result will always be +-inf: 497 # +inf * +/+inf => +inf, -inf * -/-inf => +inf 498 # +inf * -/-inf => -inf, -inf * +/+inf => -inf 499 return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/); 500 return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/); 501 return $x->binf('-'); 502 } 503 504 # x== 0 # also: or y == 1 or y == -1 505 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero(); 506 507 # According to Knuth, this can be optimized by doingtwice gcd (for d and n) 508 # and reducing in one step) 509 510 # 1 1 2 1 511 # - * - = - = - 512 # 4 3 12 6 513 514 local $Math::BigInt::accuracy = undef; 515 local $Math::BigInt::precision = undef; 516 $x->{_n}->bmul($y->{_n}); 517 $x->{_d}->bmul($y->{_d}); 518 519 # compute new sign 520 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; 521 522 $x->bnorm()->round(@r); 523 } 524 525sub bdiv 526 { 527 # (dividend: BRAT or num_str, divisor: BRAT or num_str) return 528 # (BRAT,BRAT) (quo,rem) or BRAT (only rem) 529 530 # set up parameters 531 my ($self,$x,$y,@r) = (ref($_[0]),@_); 532 # objectify is costly, so avoid it 533 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 534 { 535 ($self,$x,$y,@r) = objectify(2,@_); 536 } 537 538 $x = $self->new($x) unless $x->isa($self); 539 $y = $self->new($y) unless $y->isa($self); 540 541 return $self->_div_inf($x,$y) 542 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero()); 543 544 # x== 0 # also: or y == 1 or y == -1 545 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero(); 546 547 # TODO: list context, upgrade 548 549 # 1 1 1 3 550 # - / - == - * - 551 # 4 3 4 1 552 553 local $Math::BigInt::accuracy = undef; 554 local $Math::BigInt::precision = undef; 555 $x->{_n}->bmul($y->{_d}); 556 $x->{_d}->bmul($y->{_n}); 557 558 # compute new sign 559 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; 560 561 $x->bnorm()->round(@r); 562 $x; 563 } 564 565sub bmod 566 { 567 # compute "remainder" (in Perl way) of $x / $y 568 569 # set up parameters 570 my ($self,$x,$y,@r) = (ref($_[0]),@_); 571 # objectify is costly, so avoid it 572 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 573 { 574 ($self,$x,$y,@r) = objectify(2,@_); 575 } 576 577 $x = $self->new($x) unless $x->isa($self); 578 $y = $self->new($y) unless $y->isa($self); 579 580 return $self->_div_inf($x,$y) 581 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero()); 582 583 return $self->_div_inf($x,$y) 584 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero()); 585 586 return $x if $x->is_zero(); # 0 / 7 = 0, mod 0 587 588 # compute $x - $y * floor($x/$y), keeping the sign of $x 589 590 # locally disable these, since they would interfere 591 local $Math::BigInt::upgrade = undef; 592 local $Math::BigInt::accuracy = undef; 593 local $Math::BigInt::precision = undef; 594 595 my $u = $x->copy()->babs(); 596 # first, do a "normal" division ($x/$y) 597 $u->{_d}->bmul($y->{_n}); 598 $u->{_n}->bmul($y->{_d}); 599 600 # compute floor 601 if (!$u->{_d}->is_one()) 602 { 603 $u->{_n}->bdiv($u->{_d}); # 22/7 => 3/1 w/ truncate 604 # no need to set $u->{_d} to 1, since later we set it to $y->{_d} 605 #$x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1 606 } 607 608 # compute $y * $u 609 $u->{_d} = $y->{_d}; # 1 * $y->{_d}, see floor above 610 $u->{_n}->bmul($y->{_n}); 611 612 my $xsign = $x->{sign}; $x->{sign} = '+'; # remember sign and make abs 613 # compute $x - $u 614 $x->bsub($u); 615 $x->{sign} = $xsign; # put sign back 616 617 $x->bnorm()->round(@r); 618 } 619 620############################################################################## 621# bdec/binc 622 623sub bdec 624 { 625 # decrement value (subtract 1) 626 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 627 628 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf 629 630 local $Math::BigInt::accuracy = undef; 631 local $Math::BigInt::precision = undef; 632 if ($x->{sign} eq '-') 633 { 634 $x->{_n}->badd($x->{_d}); # -5/2 => -7/2 635 } 636 else 637 { 638 if ($x->{_n}->bacmp($x->{_d}) < 0) 639 { 640 # 1/3 -- => -2/3 641 $x->{_n} = $x->{_d} - $x->{_n}; 642 $x->{sign} = '-'; 643 } 644 else 645 { 646 $x->{_n}->bsub($x->{_d}); # 5/2 => 3/2 647 } 648 } 649 $x->bnorm()->round(@r); 650 } 651 652sub binc 653 { 654 # increment value (add 1) 655 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 656 657 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf 658 659 local $Math::BigInt::accuracy = undef; 660 local $Math::BigInt::precision = undef; 661 if ($x->{sign} eq '-') 662 { 663 if ($x->{_n}->bacmp($x->{_d}) < 0) 664 { 665 # -1/3 ++ => 2/3 (overflow at 0) 666 $x->{_n} = $x->{_d} - $x->{_n}; 667 $x->{sign} = '+'; 668 } 669 else 670 { 671 $x->{_n}->bsub($x->{_d}); # -5/2 => -3/2 672 } 673 } 674 else 675 { 676 $x->{_n}->badd($x->{_d}); # 5/2 => 7/2 677 } 678 $x->bnorm()->round(@r); 679 } 680 681############################################################################## 682# is_foo methods (the rest is inherited) 683 684sub is_int 685 { 686 # return true if arg (BRAT or num_str) is an integer 687 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 688 689 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't 690 $x->{_d}->is_one(); # x/y && y != 1 => no integer 691 0; 692 } 693 694sub is_zero 695 { 696 # return true if arg (BRAT or num_str) is zero 697 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 698 699 return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero(); 700 0; 701 } 702 703sub is_one 704 { 705 # return true if arg (BRAT or num_str) is +1 or -1 if signis given 706 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 707 708 my $sign = $_[2] || ''; $sign = '+' if $sign ne '-'; 709 return 1 710 if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one()); 711 0; 712 } 713 714sub is_odd 715 { 716 # return true if arg (BFLOAT or num_str) is odd or false if even 717 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 718 719 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't 720 ($x->{_d}->is_one() && $x->{_n}->is_odd()); # x/2 is not, but 3/1 721 0; 722 } 723 724sub is_even 725 { 726 # return true if arg (BINT or num_str) is even or false if odd 727 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 728 729 return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't 730 return 1 if ($x->{_d}->is_one() # x/3 is never 731 && $x->{_n}->is_even()); # but 4/1 is 732 0; 733 } 734 735############################################################################## 736# parts() and friends 737 738sub numerator 739 { 740 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 741 742 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/); 743 744 my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign}; 745 $n; 746 } 747 748sub denominator 749 { 750 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 751 752 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/); 753 $x->{_d}->copy(); 754 } 755 756sub parts 757 { 758 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 759 760 return ($self->bnan(),$self->bnan()) if $x->{sign} eq 'NaN'; 761 return ($self->binf(),$self->binf()) if $x->{sign} eq '+inf'; 762 return ($self->binf('-'),$self->binf()) if $x->{sign} eq '-inf'; 763 764 my $n = $x->{_n}->copy(); 765 $n->{sign} = $x->{sign}; 766 return ($n,$x->{_d}->copy()); 767 } 768 769sub length 770 { 771 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 772 773 return $nan unless $x->is_int(); 774 $x->{_n}->length(); # length(-123/1) => length(123) 775 } 776 777sub digit 778 { 779 my ($self,$x,$n) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 780 781 return $nan unless $x->is_int(); 782 $x->{_n}->digit($n); # digit(-123/1,2) => digit(123,2) 783 } 784 785############################################################################## 786# special calc routines 787 788sub bceil 789 { 790 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 791 792 return $x unless $x->{sign} =~ /^[+-]$/; 793 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0 794 795 local $Math::BigInt::upgrade = undef; 796 local $Math::BigInt::accuracy = undef; 797 local $Math::BigInt::precision = undef; 798 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate 799 $x->{_d}->bone(); 800 $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1 801 $x->{sign} = '+' if $x->{_n}->is_zero(); # -0 => 0 802 $x; 803 } 804 805sub bfloor 806 { 807 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 808 809 return $x unless $x->{sign} =~ /^[+-]$/; 810 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0 811 812 local $Math::BigInt::upgrade = undef; 813 local $Math::BigInt::accuracy = undef; 814 local $Math::BigInt::precision = undef; 815 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate 816 $x->{_d}->bone(); 817 $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1 818 $x; 819 } 820 821sub bfac 822 { 823 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 824 825 # if $x is an integer 826 if (($x->{sign} eq '+') && ($x->{_d}->is_one())) 827 { 828 $x->{_n}->bfac(); 829 return $x->round(@r); 830 } 831 $x->bnan(); 832 } 833 834sub bpow 835 { 836 # power ($x ** $y) 837 838 # set up parameters 839 my ($self,$x,$y,@r) = (ref($_[0]),@_); 840 # objectify is costly, so avoid it 841 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 842 { 843 ($self,$x,$y,@r) = objectify(2,@_); 844 } 845 846 return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x 847 return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan; 848 return $x->bone(@r) if $y->is_zero(); 849 return $x->round(@r) if $x->is_one() || $y->is_one(); 850 if ($x->{sign} eq '-' && $x->{_n}->is_one() && $x->{_d}->is_one()) 851 { 852 # if $x == -1 and odd/even y => +1/-1 853 return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r); 854 # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1; 855 } 856 # 1 ** -y => 1 / (1 ** |y|) 857 # so do test for negative $y after above's clause 858 # return $x->bnan() if $y->{sign} eq '-'; 859 return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0) 860 861 # shortcut y/1 (and/or x/1) 862 if ($y->{_d}->is_one()) 863 { 864 # shortcut for x/1 and y/1 865 if ($x->{_d}->is_one()) 866 { 867 $x->{_n}->bpow($y->{_n}); # x/1 ** y/1 => (x ** y)/1 868 if ($y->{sign} eq '-') 869 { 870 # 0.2 ** -3 => 1/(0.2 ** 3) 871 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap 872 } 873 # correct sign; + ** + => + 874 if ($x->{sign} eq '-') 875 { 876 # - * - => +, - * - * - => - 877 $x->{sign} = '+' if $y->{_n}->is_even(); 878 } 879 return $x->round(@r); 880 } 881 # x/z ** y/1 882 $x->{_n}->bpow($y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y 883 $x->{_d}->bpow($y->{_n}); 884 if ($y->{sign} eq '-') 885 { 886 # 0.2 ** -3 => 1/(0.2 ** 3) 887 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap 888 } 889 # correct sign; + ** + => + 890 if ($x->{sign} eq '-') 891 { 892 # - * - => +, - * - * - => - 893 $x->{sign} = '+' if $y->{_n}->is_even(); 894 } 895 return $x->round(@r); 896 } 897 898 # regular calculation (this is wrong for d/e ** f/g) 899 my $pow2 = $self->__one(); 900 my $y1 = $MBI->new($y->{_n}/$y->{_d})->babs(); 901 my $two = $MBI->new(2); 902 while (!$y1->is_one()) 903 { 904 $pow2->bmul($x) if $y1->is_odd(); 905 $y1->bdiv($two); 906 $x->bmul($x); 907 } 908 $x->bmul($pow2) unless $pow2->is_one(); 909 # n ** -x => 1/n ** x 910 ($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-'; 911 $x->bnorm()->round(@r); 912 } 913 914sub blog 915 { 916 # set up parameters 917 my ($self,$x,$y,@r) = (ref($_[0]),@_); 918 919 # objectify is costly, so avoid it 920 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 921 { 922 ($self,$x,$y,@r) = objectify(2,$class,@_); 923 } 924 925 # blog(1,Y) => 0 926 return $x->bzero() if $x->is_one() && $y->{sign} eq '+'; 927 928 # $x <= 0 => NaN 929 return $x->bnan() if $x->is_zero() || $x->{sign} ne '+' || $y->{sign} ne '+'; 930 931 if ($x->is_int() && $y->is_int()) 932 { 933 return $self->new($x->as_number()->blog($y->as_number(),@r)); 934 } 935 936 # do it with floats 937 $x->_new_from_float( $x->_as_float()->blog(Math::BigFloat->new("$y"),@r) ); 938 } 939 940sub _as_float 941 { 942 my $x = shift; 943 944 local $Math::BigFloat::upgrade = undef; 945 local $Math::BigFloat::accuracy = undef; 946 local $Math::BigFloat::precision = undef; 947 # 22/7 => 3.142857143.. 948 Math::BigFloat->new($x->{_n})->bdiv($x->{_d}, $x->accuracy()); 949 } 950 951sub broot 952 { 953 # set up parameters 954 my ($self,$x,$y,@r) = (ref($_[0]),@_); 955 # objectify is costly, so avoid it 956 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 957 { 958 ($self,$x,$y,@r) = objectify(2,@_); 959 } 960 961 if ($x->is_int() && $y->is_int()) 962 { 963 return $self->new($x->as_number()->broot($y->as_number(),@r)); 964 } 965 966 # do it with floats 967 $x->_new_from_float( $x->_as_float()->broot($y,@r) ); 968 } 969 970sub bmodpow 971 { 972 # set up parameters 973 my ($self,$x,$y,$m,@r) = (ref($_[0]),@_); 974 # objectify is costly, so avoid it 975 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 976 { 977 ($self,$x,$y,$m,@r) = objectify(3,@_); 978 } 979 980 # $x or $y or $m are NaN or +-inf => NaN 981 return $x->bnan() 982 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/ || 983 $m->{sign} !~ /^[+-]$/; 984 985 if ($x->is_int() && $y->is_int() && $m->is_int()) 986 { 987 return $self->new($x->as_number()->bmodpow($y->as_number(),$m,@r)); 988 } 989 990 warn ("bmodpow() not fully implemented"); 991 $x->bnan(); 992 } 993 994sub bmodinv 995 { 996 # set up parameters 997 my ($self,$x,$y,@r) = (ref($_[0]),@_); 998 # objectify is costly, so avoid it 999 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1000 { 1001 ($self,$x,$y,@r) = objectify(2,@_); 1002 } 1003 1004 # $x or $y are NaN or +-inf => NaN 1005 return $x->bnan() 1006 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/; 1007 1008 if ($x->is_int() && $y->is_int()) 1009 { 1010 return $self->new($x->as_number()->bmodinv($y->as_number(),@r)); 1011 } 1012 1013 warn ("bmodinv() not fully implemented"); 1014 $x->bnan(); 1015 } 1016 1017sub bsqrt 1018 { 1019 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 1020 1021 return $x->bnan() if $x->{sign} !~ /^[+]/; # NaN, -inf or < 0 1022 return $x if $x->{sign} eq '+inf'; # sqrt(inf) == inf 1023 return $x->round(@r) if $x->is_zero() || $x->is_one(); 1024 1025 local $Math::BigFloat::upgrade = undef; 1026 local $Math::BigFloat::downgrade = undef; 1027 local $Math::BigFloat::precision = undef; 1028 local $Math::BigFloat::accuracy = undef; 1029 local $Math::BigInt::upgrade = undef; 1030 local $Math::BigInt::precision = undef; 1031 local $Math::BigInt::accuracy = undef; 1032 1033 $x->{_d} = Math::BigFloat->new($x->{_d})->bsqrt(); 1034 $x->{_n} = Math::BigFloat->new($x->{_n})->bsqrt(); 1035 1036 # if sqrt(D) was not integer 1037 if ($x->{_d}->{_es} ne '+') 1038 { 1039 $x->{_n}->blsft($x->{_d}->exponent()->babs(),10); # 7.1/4.51 => 7.1/45.1 1040 $x->{_d} = $MBI->new($CALC->_str($x->{_d}->{_m})); # 7.1/45.1 => 71/45.1 1041 } 1042 # if sqrt(N) was not integer 1043 if ($x->{_n}->{_es} ne '+') 1044 { 1045 $x->{_d}->blsft($x->{_n}->exponent()->babs(),10); # 71/45.1 => 710/45.1 1046 $x->{_n} = $MBI->new($CALC->_str($x->{_n}->{_m})); # 710/45.1 => 710/451 1047 } 1048 1049 # convert parts to $MBI again 1050 $x->{_n} = $x->{_n}->as_number() unless $x->{_n}->isa($MBI); 1051 $x->{_d} = $x->{_d}->as_number() unless $x->{_d}->isa($MBI); 1052 $x->bnorm()->round(@r); 1053 } 1054 1055sub blsft 1056 { 1057 my ($self,$x,$y,$b,@r) = objectify(3,@_); 1058 1059 $b = 2 unless defined $b; 1060 $b = $self->new($b) unless ref ($b); 1061 $x->bmul( $b->copy()->bpow($y), @r); 1062 $x; 1063 } 1064 1065sub brsft 1066 { 1067 my ($self,$x,$y,$b,@r) = objectify(2,@_); 1068 1069 $b = 2 unless defined $b; 1070 $b = $self->new($b) unless ref ($b); 1071 $x->bdiv( $b->copy()->bpow($y), @r); 1072 $x; 1073 } 1074 1075############################################################################## 1076# round 1077 1078sub round 1079 { 1080 $_[0]; 1081 } 1082 1083sub bround 1084 { 1085 $_[0]; 1086 } 1087 1088sub bfround 1089 { 1090 $_[0]; 1091 } 1092 1093############################################################################## 1094# comparing 1095 1096sub bcmp 1097 { 1098 # compare two signed numbers 1099 1100 # set up parameters 1101 my ($self,$x,$y) = (ref($_[0]),@_); 1102 # objectify is costly, so avoid it 1103 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1104 { 1105 ($self,$x,$y) = objectify(2,@_); 1106 } 1107 1108 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/)) 1109 { 1110 # handle +-inf and NaN 1111 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan)); 1112 return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/; 1113 return +1 if $x->{sign} eq '+inf'; 1114 return -1 if $x->{sign} eq '-inf'; 1115 return -1 if $y->{sign} eq '+inf'; 1116 return +1; 1117 } 1118 # check sign for speed first 1119 return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y 1120 return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0 1121 1122 # shortcut 1123 my $xz = $x->{_n}->is_zero(); 1124 my $yz = $y->{_n}->is_zero(); 1125 return 0 if $xz && $yz; # 0 <=> 0 1126 return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y 1127 return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0 1128 1129 my $t = $x->{_n} * $y->{_d}; $t->{sign} = $x->{sign}; 1130 my $u = $y->{_n} * $x->{_d}; $u->{sign} = $y->{sign}; 1131 $t->bcmp($u); 1132 } 1133 1134sub bacmp 1135 { 1136 # compare two numbers (as unsigned) 1137 1138 # set up parameters 1139 my ($self,$x,$y) = (ref($_[0]),@_); 1140 # objectify is costly, so avoid it 1141 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1142 { 1143 ($self,$x,$y) = objectify(2,$class,@_); 1144 } 1145 1146 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/)) 1147 { 1148 # handle +-inf and NaN 1149 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan)); 1150 return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/; 1151 return 1 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/; 1152 return -1; 1153 } 1154 1155 my $t = $x->{_n} * $y->{_d}; 1156 my $u = $y->{_n} * $x->{_d}; 1157 $t->bacmp($u); 1158 } 1159 1160############################################################################## 1161# output conversation 1162 1163sub numify 1164 { 1165 # convert 17/8 => float (aka 2.125) 1166 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 1167 1168 return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, NaN, etc 1169 1170 # N/1 => N 1171 return $x->{_n}->numify() if $x->{_d}->is_one(); 1172 1173 # N/D 1174 my $neg = 1; $neg = -1 if $x->{sign} ne '+'; 1175 $neg * $x->{_n}->numify() / $x->{_d}->numify(); # return sign * N/D 1176 } 1177 1178sub as_number 1179 { 1180 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1181 1182 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc 1183 1184 # need to disable these, otherwise bdiv() gives BigRat again 1185 local $Math::BigInt::upgrade = undef; 1186 local $Math::BigInt::accuracy = undef; 1187 local $Math::BigInt::precision = undef; 1188 my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3 1189 $t->{sign} = $x->{sign}; 1190 $t; 1191 } 1192 1193sub as_bin 1194 { 1195 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1196 1197 return $x unless $x->is_int(); 1198 1199 my $s = $x->{sign}; $s = '' if $s eq '+'; 1200 $s . $x->{_n}->as_bin(); 1201 } 1202 1203sub as_hex 1204 { 1205 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1206 1207 return $x unless $x->is_int(); 1208 1209 my $s = $x->{sign}; $s = '' if $s eq '+'; 1210 $s . $x->{_n}->as_hex(); 1211 } 1212 1213sub import 1214 { 1215 my $self = shift; 1216 my $l = scalar @_; 1217 my $lib = ''; my @a; 1218 $IMPORT++; 1219 1220 for ( my $i = 0; $i < $l ; $i++) 1221 { 1222# print "at $_[$i] (",$_[$i+1]||'undef',")\n"; 1223 if ( $_[$i] eq ':constant' ) 1224 { 1225 # this rest causes overlord er load to step in 1226 # print "overload @_\n"; 1227 overload::constant float => sub { $self->new(shift); }; 1228 } 1229# elsif ($_[$i] eq 'upgrade') 1230# { 1231# # this causes upgrading 1232# $upgrade = $_[$i+1]; # or undef to disable 1233# $i++; 1234# } 1235 elsif ($_[$i] eq 'downgrade') 1236 { 1237 # this causes downgrading 1238 $downgrade = $_[$i+1]; # or undef to disable 1239 $i++; 1240 } 1241 elsif ($_[$i] eq 'lib') 1242 { 1243 $lib = $_[$i+1] || ''; # default Calc 1244 $i++; 1245 } 1246 elsif ($_[$i] eq 'with') 1247 { 1248 $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt 1249 $i++; 1250 } 1251 else 1252 { 1253 push @a, $_[$i]; 1254 } 1255 } 1256 # let use Math::BigInt lib => 'GMP'; use Math::BigRat; still work 1257 my $mbilib = eval { Math::BigInt->config()->{lib} }; 1258 if ((defined $mbilib) && ($MBI eq 'Math::BigInt')) 1259 { 1260 # MBI already loaded 1261 $MBI->import('lib',"$lib,$mbilib", 'objectify'); 1262 } 1263 else 1264 { 1265 # MBI not loaded, or not with "Math::BigInt" 1266 $lib .= ",$mbilib" if defined $mbilib; 1267 1268 if ($] < 5.006) 1269 { 1270 # Perl < 5.6.0 dies with "out of memory!" when eval() and ':constant' is 1271 # used in the same script, or eval inside import(). 1272 my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt 1273 my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm 1274 $file = File::Spec->catfile (@parts, $file); 1275 eval { require $file; $MBI->import( lib => '$lib', 'objectify' ); } 1276 } 1277 else 1278 { 1279 my $rc = "use $MBI lib => '$lib', 'objectify';"; 1280 eval $rc; 1281 } 1282 } 1283 if ($@) 1284 { 1285 require Carp; Carp::croak ("Couldn't load $MBI: $! $@"); 1286 } 1287 1288 $CALC = Math::BigFloat->config()->{lib}; 1289 1290 # any non :constant stuff is handled by our parent, Exporter 1291 # even if @_ is empty, to give it a chance 1292 $self->SUPER::import(@a); # for subclasses 1293 $self->export_to_level(1,$self,@a); # need this, too 1294 } 1295 12961; 1297 1298__END__ 1299 1300=head1 NAME 1301 1302Math::BigRat - arbitrarily big rational numbers 1303 1304=head1 SYNOPSIS 1305 1306 use Math::BigRat; 1307 1308 my $x = Math::BigRat->new('3/7'); $x += '5/9'; 1309 1310 print $x->bstr(),"\n"; 1311 print $x ** 2,"\n"; 1312 1313 my $y = Math::BigRat->new('inf'); 1314 print "$y ", ($y->is_inf ? 'is' : 'is not') , " infinity\n"; 1315 1316 my $z = Math::BigRat->new(144); $z->bsqrt(); 1317 1318=head1 DESCRIPTION 1319 1320Math::BigRat complements Math::BigInt and Math::BigFloat by providing support 1321for arbitrarily big rational numbers. 1322 1323=head2 MATH LIBRARY 1324 1325Math with the numbers is done (by default) by a module called 1326Math::BigInt::Calc. This is equivalent to saying: 1327 1328 use Math::BigRat lib => 'Calc'; 1329 1330You can change this by using: 1331 1332 use Math::BigRat lib => 'BitVect'; 1333 1334The following would first try to find Math::BigInt::Foo, then 1335Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc: 1336 1337 use Math::BigRat lib => 'Foo,Math::BigInt::Bar'; 1338 1339Calc.pm uses as internal format an array of elements of some decimal base 1340(usually 1e7, but this might be different for some systems) with the least 1341significant digit first, while BitVect.pm uses a bit vector of base 2, most 1342significant bit first. Other modules might use even different means of 1343representing the numbers. See the respective module documentation for further 1344details. 1345 1346Currently the following replacement libraries exist, search for them at CPAN: 1347 1348 Math::BigInt::BitVect 1349 Math::BigInt::GMP 1350 Math::BigInt::Pari 1351 Math::BigInt::FastCalc 1352 1353=head1 METHODS 1354 1355Any methods not listed here are dervied from Math::BigFloat (or 1356Math::BigInt), so make sure you check these two modules for further 1357information. 1358 1359=head2 new() 1360 1361 $x = Math::BigRat->new('1/3'); 1362 1363Create a new Math::BigRat object. Input can come in various forms: 1364 1365 $x = Math::BigRat->new(123); # scalars 1366 $x = Math::BigRat->new('inf'); # infinity 1367 $x = Math::BigRat->new('123.3'); # float 1368 $x = Math::BigRat->new('1/3'); # simple string 1369 $x = Math::BigRat->new('1 / 3'); # spaced 1370 $x = Math::BigRat->new('1 / 0.1'); # w/ floats 1371 $x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt 1372 $x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat 1373 $x = Math::BigRat->new(Math::BigInt::Lite->new('2')); # BigLite 1374 1375=head2 numerator() 1376 1377 $n = $x->numerator(); 1378 1379Returns a copy of the numerator (the part above the line) as signed BigInt. 1380 1381=head2 denominator() 1382 1383 $d = $x->denominator(); 1384 1385Returns a copy of the denominator (the part under the line) as positive BigInt. 1386 1387=head2 parts() 1388 1389 ($n,$d) = $x->parts(); 1390 1391Return a list consisting of (signed) numerator and (unsigned) denominator as 1392BigInts. 1393 1394=head2 as_number() 1395 1396 $x = Math::BigRat->new('13/7'); 1397 print $x->as_number(),"\n"; # '1' 1398 1399Returns a copy of the object as BigInt trunced it to integer. 1400 1401=head2 bfac() 1402 1403 $x->bfac(); 1404 1405Calculates the factorial of $x. For instance: 1406 1407 print Math::BigRat->new('3/1')->bfac(),"\n"; # 1*2*3 1408 print Math::BigRat->new('5/1')->bfac(),"\n"; # 1*2*3*4*5 1409 1410Works currently only for integers. 1411 1412=head2 blog() 1413 1414Is not yet implemented. 1415 1416=head2 bround()/round()/bfround() 1417 1418Are not yet implemented. 1419 1420=head2 bmod() 1421 1422 use Math::BigRat; 1423 my $x = Math::BigRat->new('7/4'); 1424 my $y = Math::BigRat->new('4/3'); 1425 print $x->bmod($y); 1426 1427Set $x to the remainder of the division of $x by $y. 1428 1429=head2 is_one() 1430 1431 print "$x is 1\n" if $x->is_one(); 1432 1433Return true if $x is exactly one, otherwise false. 1434 1435=head2 is_zero() 1436 1437 print "$x is 0\n" if $x->is_zero(); 1438 1439Return true if $x is exactly zero, otherwise false. 1440 1441=head2 is_positive() 1442 1443 print "$x is >= 0\n" if $x->is_positive(); 1444 1445Return true if $x is positive (greater than or equal to zero), otherwise 1446false. Please note that '+inf' is also positive, while 'NaN' and '-inf' aren't. 1447 1448=head2 is_negative() 1449 1450 print "$x is < 0\n" if $x->is_negative(); 1451 1452Return true if $x is negative (smaller than zero), otherwise false. Please 1453note that '-inf' is also negative, while 'NaN' and '+inf' aren't. 1454 1455=head2 is_int() 1456 1457 print "$x is an integer\n" if $x->is_int(); 1458 1459Return true if $x has a denominator of 1 (e.g. no fraction parts), otherwise 1460false. Please note that '-inf', 'inf' and 'NaN' aren't integer. 1461 1462=head2 is_odd() 1463 1464 print "$x is odd\n" if $x->is_odd(); 1465 1466Return true if $x is odd, otherwise false. 1467 1468=head2 is_even() 1469 1470 print "$x is even\n" if $x->is_even(); 1471 1472Return true if $x is even, otherwise false. 1473 1474=head2 bceil() 1475 1476 $x->bceil(); 1477 1478Set $x to the next bigger integer value (e.g. truncate the number to integer 1479and then increment it by one). 1480 1481=head2 bfloor() 1482 1483 $x->bfloor(); 1484 1485Truncate $x to an integer value. 1486 1487=head2 bsqrt() 1488 1489 $x->bsqrt(); 1490 1491Calculate the square root of $x. 1492 1493=head2 config 1494 1495 use Data::Dumper; 1496 1497 print Dumper ( Math::BigRat->config() ); 1498 print Math::BigRat->config()->{lib},"\n"; 1499 1500Returns a hash containing the configuration, e.g. the version number, lib 1501loaded etc. The following hash keys are currently filled in with the 1502appropriate information. 1503 1504 key RO/RW Description 1505 Example 1506 ============================================================ 1507 lib RO Name of the Math library 1508 Math::BigInt::Calc 1509 lib_version RO Version of 'lib' 1510 0.30 1511 class RO The class of config you just called 1512 Math::BigRat 1513 version RO version number of the class you used 1514 0.10 1515 upgrade RW To which class numbers are upgraded 1516 undef 1517 downgrade RW To which class numbers are downgraded 1518 undef 1519 precision RW Global precision 1520 undef 1521 accuracy RW Global accuracy 1522 undef 1523 round_mode RW Global round mode 1524 even 1525 div_scale RW Fallback acccuracy for div 1526 40 1527 trap_nan RW Trap creation of NaN (undef = no) 1528 undef 1529 trap_inf RW Trap creation of +inf/-inf (undef = no) 1530 undef 1531 1532By passing a reference to a hash you may set the configuration values. This 1533works only for values that a marked with a C<RW> above, anything else is 1534read-only. 1535 1536=head1 BUGS 1537 1538Some things are not yet implemented, or only implemented half-way: 1539 1540=over 2 1541 1542=item inf handling (partial) 1543 1544=item NaN handling (partial) 1545 1546=item rounding (not implemented except for bceil/bfloor) 1547 1548=item $x ** $y where $y is not an integer 1549 1550=item bmod(), blog(), bmodinv() and bmodpow() (partial) 1551 1552=back 1553 1554=head1 LICENSE 1555 1556This program is free software; you may redistribute it and/or modify it under 1557the same terms as Perl itself. 1558 1559=head1 SEE ALSO 1560 1561L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>, 1562L<Math::BigInt::Pari> and L<Math::BigInt::GMP>. 1563 1564See L<http://search.cpan.org/search?dist=bignum> for a way to use 1565Math::BigRat. 1566 1567The package at L<http://search.cpan.org/search?dist=Math%3A%3ABigRat> 1568may contain more documentation and examples as well as testcases. 1569 1570=head1 AUTHORS 1571 1572(C) by Tels L<http://bloodgate.com/> 2001, 2002, 2003, 2004. 1573 1574=cut 1575