[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 # You should not look at the innards of a BigRat - use the methods for this. 13 14 package Math::BigRat; 15 16 # anythig older is untested, and unlikely to work 17 use 5.006; 18 use strict; 19 20 use Math::BigFloat; 21 use vars qw($VERSION @ISA $upgrade $downgrade 22 $accuracy $precision $round_mode $div_scale $_trap_nan $_trap_inf); 23 24 @ISA = qw(Math::BigFloat); 25 26 $VERSION = '0.21'; 27 28 use overload; # inherit overload from Math::BigFloat 29 30 BEGIN 31 { 32 *objectify = \&Math::BigInt::objectify; # inherit this from BigInt 33 *AUTOLOAD = \&Math::BigFloat::AUTOLOAD; # can't inherit AUTOLOAD 34 # we inherit these from BigFloat because currently it is not possible 35 # that MBF has a different $MBI variable than we, because MBF also uses 36 # Math::BigInt::config->('lib'); (there is always only one library loaded) 37 *_e_add = \&Math::BigFloat::_e_add; 38 *_e_sub = \&Math::BigFloat::_e_sub; 39 *as_int = \&as_number; 40 *is_pos = \&is_positive; 41 *is_neg = \&is_negative; 42 } 43 44 ############################################################################## 45 # Global constants and flags. Access these only via the accessor methods! 46 47 $accuracy = $precision = undef; 48 $round_mode = 'even'; 49 $div_scale = 40; 50 $upgrade = undef; 51 $downgrade = undef; 52 53 # These are internally, and not to be used from the outside at all! 54 55 $_trap_nan = 0; # are NaNs ok? set w/ config() 56 $_trap_inf = 0; # are infs ok? set w/ config() 57 58 # the package we are using for our private parts, defaults to: 59 # Math::BigInt->config()->{lib} 60 my $MBI = 'Math::BigInt::Calc'; 61 62 my $nan = 'NaN'; 63 my $class = 'Math::BigRat'; 64 65 sub isa 66 { 67 return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't 68 UNIVERSAL::isa(@_); 69 } 70 71 ############################################################################## 72 73 sub _new_from_float 74 { 75 # turn a single float input into a rational number (like '0.1') 76 my ($self,$f) = @_; 77 78 return $self->bnan() if $f->is_nan(); 79 return $self->binf($f->{sign}) if $f->{sign} =~ /^[+-]inf$/; 80 81 $self->{_n} = $MBI->_copy( $f->{_m} ); # mantissa 82 $self->{_d} = $MBI->_one(); 83 $self->{sign} = $f->{sign} || '+'; 84 if ($f->{_es} eq '-') 85 { 86 # something like Math::BigRat->new('0.1'); 87 # 1 / 1 => 1/10 88 $MBI->_lsft ( $self->{_d}, $f->{_e} ,10); 89 } 90 else 91 { 92 # something like Math::BigRat->new('10'); 93 # 1 / 1 => 10/1 94 $MBI->_lsft ( $self->{_n}, $f->{_e} ,10) unless 95 $MBI->_is_zero($f->{_e}); 96 } 97 $self; 98 } 99 100 sub new 101 { 102 # create a Math::BigRat 103 my $class = shift; 104 105 my ($n,$d) = @_; 106 107 my $self = { }; bless $self,$class; 108 109 # input like (BigInt) or (BigFloat): 110 if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat'))) 111 { 112 if ($n->isa('Math::BigFloat')) 113 { 114 $self->_new_from_float($n); 115 } 116 if ($n->isa('Math::BigInt')) 117 { 118 # TODO: trap NaN, inf 119 $self->{_n} = $MBI->_copy($n->{value}); # "mantissa" = N 120 $self->{_d} = $MBI->_one(); # d => 1 121 $self->{sign} = $n->{sign}; 122 } 123 if ($n->isa('Math::BigInt::Lite')) 124 { 125 # TODO: trap NaN, inf 126 $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0; 127 $self->{_n} = $MBI->_new(abs($$n)); # "mantissa" = N 128 $self->{_d} = $MBI->_one(); # d => 1 129 } 130 return $self->bnorm(); # normalize (120/1 => 12/10) 131 } 132 133 # input like (BigInt,BigInt) or (BigLite,BigLite): 134 if (ref($d) && ref($n)) 135 { 136 # do N first (for $self->{sign}): 137 if ($n->isa('Math::BigInt')) 138 { 139 # TODO: trap NaN, inf 140 $self->{_n} = $MBI->_copy($n->{value}); # "mantissa" = N 141 $self->{sign} = $n->{sign}; 142 } 143 elsif ($n->isa('Math::BigInt::Lite')) 144 { 145 # TODO: trap NaN, inf 146 $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0; 147 $self->{_n} = $MBI->_new(abs($$n)); # "mantissa" = $n 148 } 149 else 150 { 151 require Carp; 152 Carp::croak(ref($n) . " is not a recognized object format for Math::BigRat->new"); 153 } 154 # now D: 155 if ($d->isa('Math::BigInt')) 156 { 157 # TODO: trap NaN, inf 158 $self->{_d} = $MBI->_copy($d->{value}); # "mantissa" = D 159 # +/+ or -/- => +, +/- or -/+ => - 160 $self->{sign} = $d->{sign} ne $self->{sign} ? '-' : '+'; 161 } 162 elsif ($d->isa('Math::BigInt::Lite')) 163 { 164 # TODO: trap NaN, inf 165 $self->{_d} = $MBI->_new(abs($$d)); # "mantissa" = D 166 my $ds = '+'; $ds = '-' if $$d < 0; 167 # +/+ or -/- => +, +/- or -/+ => - 168 $self->{sign} = $ds ne $self->{sign} ? '-' : '+'; 169 } 170 else 171 { 172 require Carp; 173 Carp::croak(ref($d) . " is not a recognized object format for Math::BigRat->new"); 174 } 175 return $self->bnorm(); # normalize (120/1 => 12/10) 176 } 177 return $n->copy() if ref $n; # already a BigRat 178 179 if (!defined $n) 180 { 181 $self->{_n} = $MBI->_zero(); # undef => 0 182 $self->{_d} = $MBI->_one(); 183 $self->{sign} = '+'; 184 return $self; 185 } 186 187 # string input with / delimiter 188 if ($n =~ /\s*\/\s*/) 189 { 190 return $class->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid 191 return $class->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid 192 ($n,$d) = split (/\//,$n); 193 # try as BigFloats first 194 if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/)) 195 { 196 local $Math::BigFloat::accuracy = undef; 197 local $Math::BigFloat::precision = undef; 198 199 # one of them looks like a float 200 my $nf = Math::BigFloat->new($n,undef,undef); 201 $self->{sign} = '+'; 202 return $self->bnan() if $nf->is_nan(); 203 204 $self->{_n} = $MBI->_copy( $nf->{_m} ); # get mantissa 205 206 # now correct $self->{_n} due to $n 207 my $f = Math::BigFloat->new($d,undef,undef); 208 return $self->bnan() if $f->is_nan(); 209 $self->{_d} = $MBI->_copy( $f->{_m} ); 210 211 # calculate the difference between nE and dE 212 my $diff_e = $nf->exponent()->bsub( $f->exponent); 213 if ($diff_e->is_negative()) 214 { 215 # < 0: mul d with it 216 $MBI->_lsft( $self->{_d}, $MBI->_new( $diff_e->babs()), 10); 217 } 218 elsif (!$diff_e->is_zero()) 219 { 220 # > 0: mul n with it 221 $MBI->_lsft( $self->{_n}, $MBI->_new( $diff_e), 10); 222 } 223 } 224 else 225 { 226 # both d and n look like (big)ints 227 228 $self->{sign} = '+'; # no sign => '+' 229 $self->{_n} = undef; 230 $self->{_d} = undef; 231 if ($n =~ /^([+-]?)0*([0-9]+)\z/) # first part ok? 232 { 233 $self->{sign} = $1 || '+'; # no sign => '+' 234 $self->{_n} = $MBI->_new($2 || 0); 235 } 236 237 if ($d =~ /^([+-]?)0*([0-9]+)\z/) # second part ok? 238 { 239 $self->{sign} =~ tr/+-/-+/ if ($1 || '') eq '-'; # negate if second part neg. 240 $self->{_d} = $MBI->_new($2 || 0); 241 } 242 243 if (!defined $self->{_n} || !defined $self->{_d}) 244 { 245 $d = Math::BigInt->new($d,undef,undef) unless ref $d; 246 $n = Math::BigInt->new($n,undef,undef) unless ref $n; 247 248 if ($n->{sign} =~ /^[+-]$/ && $d->{sign} =~ /^[+-]$/) 249 { 250 # both parts are ok as integers (wierd things like ' 1e0' 251 $self->{_n} = $MBI->_copy($n->{value}); 252 $self->{_d} = $MBI->_copy($d->{value}); 253 $self->{sign} = $n->{sign}; 254 $self->{sign} =~ tr/+-/-+/ if $d->{sign} eq '-'; # -1/-2 => 1/2 255 return $self->bnorm(); 256 } 257 258 $self->{sign} = '+'; # a default sign 259 return $self->bnan() if $n->is_nan() || $d->is_nan(); 260 261 # handle inf cases: 262 if ($n->is_inf() || $d->is_inf()) 263 { 264 if ($n->is_inf()) 265 { 266 return $self->bnan() if $d->is_inf(); # both are inf => NaN 267 my $s = '+'; # '+inf/+123' or '-inf/-123' 268 $s = '-' if substr($n->{sign},0,1) ne $d->{sign}; 269 # +-inf/123 => +-inf 270 return $self->binf($s); 271 } 272 # 123/inf => 0 273 return $self->bzero(); 274 } 275 } 276 } 277 278 return $self->bnorm(); 279 } 280 281 # simple string input 282 if (($n =~ /[\.eE]/)) 283 { 284 # looks like a float, quacks like a float, so probably is a float 285 $self->{sign} = 'NaN'; 286 local $Math::BigFloat::accuracy = undef; 287 local $Math::BigFloat::precision = undef; 288 $self->_new_from_float(Math::BigFloat->new($n,undef,undef)); 289 } 290 else 291 { 292 # for simple forms, use $MBI directly 293 if ($n =~ /^([+-]?)0*([0-9]+)\z/) 294 { 295 $self->{sign} = $1 || '+'; 296 $self->{_n} = $MBI->_new($2 || 0); 297 $self->{_d} = $MBI->_one(); 298 } 299 else 300 { 301 my $n = Math::BigInt->new($n,undef,undef); 302 $self->{_n} = $MBI->_copy($n->{value}); 303 $self->{_d} = $MBI->_one(); 304 $self->{sign} = $n->{sign}; 305 return $self->bnan() if $self->{sign} eq 'NaN'; 306 return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/; 307 } 308 } 309 $self->bnorm(); 310 } 311 312 sub copy 313 { 314 # if two arguments, the first one is the class to "swallow" subclasses 315 my ($c,$x) = @_; 316 317 if (scalar @_ == 1) 318 { 319 $x = $_[0]; 320 $c = ref($x); 321 } 322 return unless ref($x); # only for objects 323 324 my $self = bless {}, $c; 325 326 $self->{sign} = $x->{sign}; 327 $self->{_d} = $MBI->_copy($x->{_d}); 328 $self->{_n} = $MBI->_copy($x->{_n}); 329 $self->{_a} = $x->{_a} if defined $x->{_a}; 330 $self->{_p} = $x->{_p} if defined $x->{_p}; 331 $self; 332 } 333 334 ############################################################################## 335 336 sub config 337 { 338 # return (later set?) configuration data as hash ref 339 my $class = shift || 'Math::BigRat'; 340 341 if (@_ == 1 && ref($_[0]) ne 'HASH') 342 { 343 my $cfg = $class->SUPER::config(); 344 return $cfg->{$_[0]}; 345 } 346 347 my $cfg = $class->SUPER::config(@_); 348 349 # now we need only to override the ones that are different from our parent 350 $cfg->{class} = $class; 351 $cfg->{with} = $MBI; 352 $cfg; 353 } 354 355 ############################################################################## 356 357 sub bstr 358 { 359 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 360 361 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc 362 { 363 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf 364 return $s; 365 } 366 367 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2' 368 369 return $s . $MBI->_str($x->{_n}) if $MBI->_is_one($x->{_d}); 370 $s . $MBI->_str($x->{_n}) . '/' . $MBI->_str($x->{_d}); 371 } 372 373 sub bsstr 374 { 375 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 376 377 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc 378 { 379 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf 380 return $s; 381 } 382 383 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3 384 $s . $MBI->_str($x->{_n}) . '/' . $MBI->_str($x->{_d}); 385 } 386 387 sub bnorm 388 { 389 # reduce the number to the shortest form 390 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 391 392 # Both parts must be objects of whatever we are using today. 393 if ( my $c = $MBI->_check($x->{_n}) ) 394 { 395 require Carp; Carp::croak ("n did not pass the self-check ($c) in bnorm()"); 396 } 397 if ( my $c = $MBI->_check($x->{_d}) ) 398 { 399 require Carp; Carp::croak ("d did not pass the self-check ($c) in bnorm()"); 400 } 401 402 # no normalize for NaN, inf etc. 403 return $x if $x->{sign} !~ /^[+-]$/; 404 405 # normalize zeros to 0/1 406 if ($MBI->_is_zero($x->{_n})) 407 { 408 $x->{sign} = '+'; # never leave a -0 409 $x->{_d} = $MBI->_one() unless $MBI->_is_one($x->{_d}); 410 return $x; 411 } 412 413 return $x if $MBI->_is_one($x->{_d}); # no need to reduce 414 415 # reduce other numbers 416 my $gcd = $MBI->_copy($x->{_n}); 417 $gcd = $MBI->_gcd($gcd,$x->{_d}); 418 419 if (!$MBI->_is_one($gcd)) 420 { 421 $x->{_n} = $MBI->_div($x->{_n},$gcd); 422 $x->{_d} = $MBI->_div($x->{_d},$gcd); 423 } 424 $x; 425 } 426 427 ############################################################################## 428 # sign manipulation 429 430 sub bneg 431 { 432 # (BRAT or num_str) return BRAT 433 # negate number or make a negated number from string 434 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 435 436 return $x if $x->modify('bneg'); 437 438 # for +0 dont negate (to have always normalized +0). Does nothing for 'NaN' 439 $x->{sign} =~ tr/+-/-+/ unless ($x->{sign} eq '+' && $MBI->_is_zero($x->{_n})); 440 $x; 441 } 442 443 ############################################################################## 444 # special values 445 446 sub _bnan 447 { 448 # used by parent class bnan() to initialize number to NaN 449 my $self = shift; 450 451 if ($_trap_nan) 452 { 453 require Carp; 454 my $class = ref($self); 455 # "$self" below will stringify the object, this blows up if $self is a 456 # partial object (happens under trap_nan), so fix it beforehand 457 $self->{_d} = $MBI->_zero() unless defined $self->{_d}; 458 $self->{_n} = $MBI->_zero() unless defined $self->{_n}; 459 Carp::croak ("Tried to set $self to NaN in $class\::_bnan()"); 460 } 461 $self->{_n} = $MBI->_zero(); 462 $self->{_d} = $MBI->_zero(); 463 } 464 465 sub _binf 466 { 467 # used by parent class bone() to initialize number to +inf/-inf 468 my $self = shift; 469 470 if ($_trap_inf) 471 { 472 require Carp; 473 my $class = ref($self); 474 # "$self" below will stringify the object, this blows up if $self is a 475 # partial object (happens under trap_nan), so fix it beforehand 476 $self->{_d} = $MBI->_zero() unless defined $self->{_d}; 477 $self->{_n} = $MBI->_zero() unless defined $self->{_n}; 478 Carp::croak ("Tried to set $self to inf in $class\::_binf()"); 479 } 480 $self->{_n} = $MBI->_zero(); 481 $self->{_d} = $MBI->_zero(); 482 } 483 484 sub _bone 485 { 486 # used by parent class bone() to initialize number to +1/-1 487 my $self = shift; 488 $self->{_n} = $MBI->_one(); 489 $self->{_d} = $MBI->_one(); 490 } 491 492 sub _bzero 493 { 494 # used by parent class bzero() to initialize number to 0 495 my $self = shift; 496 $self->{_n} = $MBI->_zero(); 497 $self->{_d} = $MBI->_one(); 498 } 499 500 ############################################################################## 501 # mul/add/div etc 502 503 sub badd 504 { 505 # add two rational numbers 506 507 # set up parameters 508 my ($self,$x,$y,@r) = (ref($_[0]),@_); 509 # objectify is costly, so avoid it 510 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 511 { 512 ($self,$x,$y,@r) = objectify(2,@_); 513 } 514 515 # +inf + +inf => +inf, -inf + -inf => -inf 516 return $x->binf(substr($x->{sign},0,1)) 517 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/; 518 519 # +inf + -inf or -inf + +inf => NaN 520 return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/); 521 522 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7 523 # - + - = --------- = -- 524 # 4 3 4*3 12 525 526 # we do not compute the gcd() here, but simple do: 527 # 5 7 5*3 + 7*4 43 528 # - + - = --------- = -- 529 # 4 3 4*3 12 530 531 # and bnorm() will then take care of the rest 532 533 # 5 * 3 534 $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_d}); 535 536 # 7 * 4 537 my $m = $MBI->_mul( $MBI->_copy( $y->{_n} ), $x->{_d} ); 538 539 # 5 * 3 + 7 * 4 540 ($x->{_n}, $x->{sign}) = _e_add( $x->{_n}, $m, $x->{sign}, $y->{sign}); 541 542 # 4 * 3 543 $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_d}); 544 545 # normalize result, and possible round 546 $x->bnorm()->round(@r); 547 } 548 549 sub bsub 550 { 551 # subtract two rational numbers 552 553 # set up parameters 554 my ($self,$x,$y,@r) = (ref($_[0]),@_); 555 # objectify is costly, so avoid it 556 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 557 { 558 ($self,$x,$y,@r) = objectify(2,@_); 559 } 560 561 # flip sign of $x, call badd(), then flip sign of result 562 $x->{sign} =~ tr/+-/-+/ 563 unless $x->{sign} eq '+' && $MBI->_is_zero($x->{_n}); # not -0 564 $x->badd($y,@r); # does norm and round 565 $x->{sign} =~ tr/+-/-+/ 566 unless $x->{sign} eq '+' && $MBI->_is_zero($x->{_n}); # not -0 567 $x; 568 } 569 570 sub bmul 571 { 572 # multiply two rational numbers 573 574 # set up parameters 575 my ($self,$x,$y,@r) = (ref($_[0]),@_); 576 # objectify is costly, so avoid it 577 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 578 { 579 ($self,$x,$y,@r) = objectify(2,@_); 580 } 581 582 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN'); 583 584 # inf handling 585 if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/)) 586 { 587 return $x->bnan() if $x->is_zero() || $y->is_zero(); 588 # result will always be +-inf: 589 # +inf * +/+inf => +inf, -inf * -/-inf => +inf 590 # +inf * -/-inf => -inf, -inf * +/+inf => -inf 591 return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/); 592 return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/); 593 return $x->binf('-'); 594 } 595 596 # x== 0 # also: or y == 1 or y == -1 597 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero(); 598 599 # XXX TODO: 600 # According to Knuth, this can be optimized by doing gcd twice (for d and n) 601 # and reducing in one step. This would save us the bnorm() at the end. 602 603 # 1 2 1 * 2 2 1 604 # - * - = ----- = - = - 605 # 4 3 4 * 3 12 6 606 607 $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_n}); 608 $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_d}); 609 610 # compute new sign 611 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; 612 613 $x->bnorm()->round(@r); 614 } 615 616 sub bdiv 617 { 618 # (dividend: BRAT or num_str, divisor: BRAT or num_str) return 619 # (BRAT,BRAT) (quo,rem) or BRAT (only rem) 620 621 # set up parameters 622 my ($self,$x,$y,@r) = (ref($_[0]),@_); 623 # objectify is costly, so avoid it 624 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 625 { 626 ($self,$x,$y,@r) = objectify(2,@_); 627 } 628 629 return $self->_div_inf($x,$y) 630 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero()); 631 632 # x== 0 # also: or y == 1 or y == -1 633 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero(); 634 635 # XXX TODO: list context, upgrade 636 # According to Knuth, this can be optimized by doing gcd twice (for d and n) 637 # and reducing in one step. This would save us the bnorm() at the end. 638 639 # 1 1 1 3 640 # - / - == - * - 641 # 4 3 4 1 642 643 $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_d}); 644 $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_n}); 645 646 # compute new sign 647 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; 648 649 $x->bnorm()->round(@r); 650 $x; 651 } 652 653 sub bmod 654 { 655 # compute "remainder" (in Perl way) of $x / $y 656 657 # set up parameters 658 my ($self,$x,$y,@r) = (ref($_[0]),@_); 659 # objectify is costly, so avoid it 660 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 661 { 662 ($self,$x,$y,@r) = objectify(2,@_); 663 } 664 665 return $self->_div_inf($x,$y) 666 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero()); 667 668 return $x if $x->is_zero(); # 0 / 7 = 0, mod 0 669 670 # compute $x - $y * floor($x/$y), keeping the sign of $x 671 672 # copy x to u, make it positive and then do a normal division ($u/$y) 673 my $u = bless { sign => '+' }, $self; 674 $u->{_n} = $MBI->_mul( $MBI->_copy($x->{_n}), $y->{_d} ); 675 $u->{_d} = $MBI->_mul( $MBI->_copy($x->{_d}), $y->{_n} ); 676 677 # compute floor(u) 678 if (! $MBI->_is_one($u->{_d})) 679 { 680 $u->{_n} = $MBI->_div($u->{_n},$u->{_d}); # 22/7 => 3/1 w/ truncate 681 # no need to set $u->{_d} to 1, since below we set it to $y->{_d} anyway 682 } 683 684 # now compute $y * $u 685 $u->{_d} = $MBI->_copy($y->{_d}); # 1 * $y->{_d}, see floor above 686 $u->{_n} = $MBI->_mul($u->{_n},$y->{_n}); 687 688 my $xsign = $x->{sign}; $x->{sign} = '+'; # remember sign and make x positive 689 # compute $x - $u 690 $x->bsub($u); 691 $x->{sign} = $xsign; # put sign back 692 693 $x->bnorm()->round(@r); 694 } 695 696 ############################################################################## 697 # bdec/binc 698 699 sub bdec 700 { 701 # decrement value (subtract 1) 702 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 703 704 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf 705 706 if ($x->{sign} eq '-') 707 { 708 $x->{_n} = $MBI->_add( $x->{_n}, $x->{_d}); # -5/2 => -7/2 709 } 710 else 711 { 712 if ($MBI->_acmp($x->{_n},$x->{_d}) < 0) # n < d? 713 { 714 # 1/3 -- => -2/3 715 $x->{_n} = $MBI->_sub( $MBI->_copy($x->{_d}), $x->{_n}); 716 $x->{sign} = '-'; 717 } 718 else 719 { 720 $x->{_n} = $MBI->_sub($x->{_n}, $x->{_d}); # 5/2 => 3/2 721 } 722 } 723 $x->bnorm()->round(@r); 724 } 725 726 sub binc 727 { 728 # increment value (add 1) 729 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 730 731 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf 732 733 if ($x->{sign} eq '-') 734 { 735 if ($MBI->_acmp($x->{_n},$x->{_d}) < 0) 736 { 737 # -1/3 ++ => 2/3 (overflow at 0) 738 $x->{_n} = $MBI->_sub( $MBI->_copy($x->{_d}), $x->{_n}); 739 $x->{sign} = '+'; 740 } 741 else 742 { 743 $x->{_n} = $MBI->_sub($x->{_n}, $x->{_d}); # -5/2 => -3/2 744 } 745 } 746 else 747 { 748 $x->{_n} = $MBI->_add($x->{_n},$x->{_d}); # 5/2 => 7/2 749 } 750 $x->bnorm()->round(@r); 751 } 752 753 ############################################################################## 754 # is_foo methods (the rest is inherited) 755 756 sub is_int 757 { 758 # return true if arg (BRAT or num_str) is an integer 759 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 760 761 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't 762 $MBI->_is_one($x->{_d}); # x/y && y != 1 => no integer 763 0; 764 } 765 766 sub is_zero 767 { 768 # return true if arg (BRAT or num_str) is zero 769 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 770 771 return 1 if $x->{sign} eq '+' && $MBI->_is_zero($x->{_n}); 772 0; 773 } 774 775 sub is_one 776 { 777 # return true if arg (BRAT or num_str) is +1 or -1 if signis given 778 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 779 780 my $sign = $_[2] || ''; $sign = '+' if $sign ne '-'; 781 return 1 782 if ($x->{sign} eq $sign && $MBI->_is_one($x->{_n}) && $MBI->_is_one($x->{_d})); 783 0; 784 } 785 786 sub is_odd 787 { 788 # return true if arg (BFLOAT or num_str) is odd or false if even 789 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 790 791 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't 792 ($MBI->_is_one($x->{_d}) && $MBI->_is_odd($x->{_n})); # x/2 is not, but 3/1 793 0; 794 } 795 796 sub is_even 797 { 798 # return true if arg (BINT or num_str) is even or false if odd 799 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 800 801 return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't 802 return 1 if ($MBI->_is_one($x->{_d}) # x/3 is never 803 && $MBI->_is_even($x->{_n})); # but 4/1 is 804 0; 805 } 806 807 ############################################################################## 808 # parts() and friends 809 810 sub numerator 811 { 812 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 813 814 # NaN, inf, -inf 815 return Math::BigInt->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/); 816 817 my $n = Math::BigInt->new($MBI->_str($x->{_n})); $n->{sign} = $x->{sign}; 818 $n; 819 } 820 821 sub denominator 822 { 823 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 824 825 # NaN 826 return Math::BigInt->new($x->{sign}) if $x->{sign} eq 'NaN'; 827 # inf, -inf 828 return Math::BigInt->bone() if $x->{sign} !~ /^[+-]$/; 829 830 Math::BigInt->new($MBI->_str($x->{_d})); 831 } 832 833 sub parts 834 { 835 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 836 837 my $c = 'Math::BigInt'; 838 839 return ($c->bnan(),$c->bnan()) if $x->{sign} eq 'NaN'; 840 return ($c->binf(),$c->binf()) if $x->{sign} eq '+inf'; 841 return ($c->binf('-'),$c->binf()) if $x->{sign} eq '-inf'; 842 843 my $n = $c->new( $MBI->_str($x->{_n})); 844 $n->{sign} = $x->{sign}; 845 my $d = $c->new( $MBI->_str($x->{_d})); 846 ($n,$d); 847 } 848 849 sub length 850 { 851 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 852 853 return $nan unless $x->is_int(); 854 $MBI->_len($x->{_n}); # length(-123/1) => length(123) 855 } 856 857 sub digit 858 { 859 my ($self,$x,$n) = ref($_[0]) ? (undef,$_[0],$_[1]) : objectify(1,@_); 860 861 return $nan unless $x->is_int(); 862 $MBI->_digit($x->{_n},$n || 0); # digit(-123/1,2) => digit(123,2) 863 } 864 865 ############################################################################## 866 # special calc routines 867 868 sub bceil 869 { 870 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 871 872 return $x if $x->{sign} !~ /^[+-]$/ || # not for NaN, inf 873 $MBI->_is_one($x->{_d}); # 22/1 => 22, 0/1 => 0 874 875 $x->{_n} = $MBI->_div($x->{_n},$x->{_d}); # 22/7 => 3/1 w/ truncate 876 $x->{_d} = $MBI->_one(); # d => 1 877 $x->{_n} = $MBI->_inc($x->{_n}) 878 if $x->{sign} eq '+'; # +22/7 => 4/1 879 $x->{sign} = '+' if $MBI->_is_zero($x->{_n}); # -0 => 0 880 $x; 881 } 882 883 sub bfloor 884 { 885 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); 886 887 return $x if $x->{sign} !~ /^[+-]$/ || # not for NaN, inf 888 $MBI->_is_one($x->{_d}); # 22/1 => 22, 0/1 => 0 889 890 $x->{_n} = $MBI->_div($x->{_n},$x->{_d}); # 22/7 => 3/1 w/ truncate 891 $x->{_d} = $MBI->_one(); # d => 1 892 $x->{_n} = $MBI->_inc($x->{_n}) 893 if $x->{sign} eq '-'; # -22/7 => -4/1 894 $x; 895 } 896 897 sub bfac 898 { 899 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 900 901 # if $x is not an integer 902 if (($x->{sign} ne '+') || (!$MBI->_is_one($x->{_d}))) 903 { 904 return $x->bnan(); 905 } 906 907 $x->{_n} = $MBI->_fac($x->{_n}); 908 # since _d is 1, we don't need to reduce/norm the result 909 $x->round(@r); 910 } 911 912 sub bpow 913 { 914 # power ($x ** $y) 915 916 # set up parameters 917 my ($self,$x,$y,@r) = (ref($_[0]),@_); 918 # objectify is costly, so avoid it 919 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 920 { 921 ($self,$x,$y,@r) = objectify(2,@_); 922 } 923 924 return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x 925 return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan; 926 return $x->bone(@r) if $y->is_zero(); 927 return $x->round(@r) if $x->is_one() || $y->is_one(); 928 929 if ($x->{sign} eq '-' && $MBI->_is_one($x->{_n}) && $MBI->_is_one($x->{_d})) 930 { 931 # if $x == -1 and odd/even y => +1/-1 932 return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r); 933 # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1; 934 } 935 # 1 ** -y => 1 / (1 ** |y|) 936 # so do test for negative $y after above's clause 937 938 return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0) 939 940 # shortcut y/1 (and/or x/1) 941 if ($MBI->_is_one($y->{_d})) 942 { 943 # shortcut for x/1 and y/1 944 if ($MBI->_is_one($x->{_d})) 945 { 946 $x->{_n} = $MBI->_pow($x->{_n},$y->{_n}); # x/1 ** y/1 => (x ** y)/1 947 if ($y->{sign} eq '-') 948 { 949 # 0.2 ** -3 => 1/(0.2 ** 3) 950 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap 951 } 952 # correct sign; + ** + => + 953 if ($x->{sign} eq '-') 954 { 955 # - * - => +, - * - * - => - 956 $x->{sign} = '+' if $MBI->_is_even($y->{_n}); 957 } 958 return $x->round(@r); 959 } 960 # x/z ** y/1 961 $x->{_n} = $MBI->_pow($x->{_n},$y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y 962 $x->{_d} = $MBI->_pow($x->{_d},$y->{_n}); 963 if ($y->{sign} eq '-') 964 { 965 # 0.2 ** -3 => 1/(0.2 ** 3) 966 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap 967 } 968 # correct sign; + ** + => + 969 if ($x->{sign} eq '-') 970 { 971 # - * - => +, - * - * - => - 972 $x->{sign} = '+' if $MBI->_is_even($y->{_n}); 973 } 974 return $x->round(@r); 975 } 976 977 # regular calculation (this is wrong for d/e ** f/g) 978 my $pow2 = $self->bone(); 979 my $y1 = $MBI->_div ( $MBI->_copy($y->{_n}), $y->{_d}); 980 my $two = $MBI->_two(); 981 982 while (!$MBI->_is_one($y1)) 983 { 984 $pow2->bmul($x) if $MBI->_is_odd($y1); 985 $MBI->_div($y1, $two); 986 $x->bmul($x); 987 } 988 $x->bmul($pow2) unless $pow2->is_one(); 989 # n ** -x => 1/n ** x 990 ($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-'; 991 $x->bnorm()->round(@r); 992 } 993 994 sub blog 995 { 996 # set up parameters 997 my ($self,$x,$y,@r) = (ref($_[0]),@_); 998 999 # objectify is costly, so avoid it 1000 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1001 { 1002 ($self,$x,$y,@r) = objectify(2,$class,@_); 1003 } 1004 1005 # blog(1,Y) => 0 1006 return $x->bzero() if $x->is_one() && $y->{sign} eq '+'; 1007 1008 # $x <= 0 => NaN 1009 return $x->bnan() if $x->is_zero() || $x->{sign} ne '+' || $y->{sign} ne '+'; 1010 1011 if ($x->is_int() && $y->is_int()) 1012 { 1013 return $self->new($x->as_number()->blog($y->as_number(),@r)); 1014 } 1015 1016 # do it with floats 1017 $x->_new_from_float( $x->_as_float()->blog(Math::BigFloat->new("$y"),@r) ); 1018 } 1019 1020 sub bexp 1021 { 1022 # set up parameters 1023 my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_); 1024 1025 # objectify is costly, so avoid it 1026 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1027 { 1028 ($self,$x,$y,$a,$p,$r) = objectify(2,$class,@_); 1029 } 1030 1031 return $x->binf() if $x->{sign} eq '+inf'; 1032 return $x->bzero() if $x->{sign} eq '-inf'; 1033 1034 # we need to limit the accuracy to protect against overflow 1035 my $fallback = 0; 1036 my ($scale,@params); 1037 ($x,@params) = $x->_find_round_parameters($a,$p,$r); 1038 1039 # also takes care of the "error in _find_round_parameters?" case 1040 return $x if $x->{sign} eq 'NaN'; 1041 1042 # no rounding at all, so must use fallback 1043 if (scalar @params == 0) 1044 { 1045 # simulate old behaviour 1046 $params[0] = $self->div_scale(); # and round to it as accuracy 1047 $params[1] = undef; # P = undef 1048 $scale = $params[0]+4; # at least four more for proper round 1049 $params[2] = $r; # round mode by caller or undef 1050 $fallback = 1; # to clear a/p afterwards 1051 } 1052 else 1053 { 1054 # the 4 below is empirical, and there might be cases where it's not enough... 1055 $scale = abs($params[0] || $params[1]) + 4; # take whatever is defined 1056 } 1057 1058 return $x->bone(@params) if $x->is_zero(); 1059 1060 # See the comments in Math::BigFloat on how this algorithm works. 1061 # Basically we calculate A and B (where B is faculty(N)) so that A/B = e 1062 1063 my $x_org = $x->copy(); 1064 if ($scale <= 75) 1065 { 1066 # set $x directly from a cached string form 1067 $x->{_n} = $MBI->_new("90933395208605785401971970164779391644753259799242"); 1068 $x->{_d} = $MBI->_new("33452526613163807108170062053440751665152000000000"); 1069 $x->{sign} = '+'; 1070 } 1071 else 1072 { 1073 # compute A and B so that e = A / B. 1074 1075 # After some terms we end up with this, so we use it as a starting point: 1076 my $A = $MBI->_new("90933395208605785401971970164779391644753259799242"); 1077 my $F = $MBI->_new(42); my $step = 42; 1078 1079 # Compute how many steps we need to take to get $A and $B sufficiently big 1080 my $steps = Math::BigFloat::_len_to_steps($scale - 4); 1081 # print STDERR "# Doing $steps steps for ", $scale-4, " digits\n"; 1082 while ($step++ <= $steps) 1083 { 1084 # calculate $a * $f + 1 1085 $A = $MBI->_mul($A, $F); 1086 $A = $MBI->_inc($A); 1087 # increment f 1088 $F = $MBI->_inc($F); 1089 } 1090 # compute $B as factorial of $steps (this is faster than doing it manually) 1091 my $B = $MBI->_fac($MBI->_new($steps)); 1092 1093 # print "A ", $MBI->_str($A), "\nB ", $MBI->_str($B), "\n"; 1094 1095 $x->{_n} = $A; 1096 $x->{_d} = $B; 1097 $x->{sign} = '+'; 1098 } 1099 1100 # $x contains now an estimate of e, with some surplus digits, so we can round 1101 if (!$x_org->is_one()) 1102 { 1103 # raise $x to the wanted power and round it in one step: 1104 $x->bpow($x_org, @params); 1105 } 1106 else 1107 { 1108 # else just round the already computed result 1109 delete $x->{_a}; delete $x->{_p}; 1110 # shortcut to not run through _find_round_parameters again 1111 if (defined $params[0]) 1112 { 1113 $x->bround($params[0],$params[2]); # then round accordingly 1114 } 1115 else 1116 { 1117 $x->bfround($params[1],$params[2]); # then round accordingly 1118 } 1119 } 1120 if ($fallback) 1121 { 1122 # clear a/p after round, since user did not request it 1123 delete $x->{_a}; delete $x->{_p}; 1124 } 1125 1126 $x; 1127 } 1128 1129 sub bnok 1130 { 1131 # set up parameters 1132 my ($self,$x,$y,@r) = (ref($_[0]),@_); 1133 1134 # objectify is costly, so avoid it 1135 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1136 { 1137 ($self,$x,$y,@r) = objectify(2,$class,@_); 1138 } 1139 1140 # do it with floats 1141 $x->_new_from_float( $x->_as_float()->bnok(Math::BigFloat->new("$y"),@r) ); 1142 } 1143 1144 sub _float_from_part 1145 { 1146 my $x = shift; 1147 1148 my $f = Math::BigFloat->bzero(); 1149 $f->{_m} = $MBI->_copy($x); 1150 $f->{_e} = $MBI->_zero(); 1151 1152 $f; 1153 } 1154 1155 sub _as_float 1156 { 1157 my $x = shift; 1158 1159 local $Math::BigFloat::upgrade = undef; 1160 local $Math::BigFloat::accuracy = undef; 1161 local $Math::BigFloat::precision = undef; 1162 # 22/7 => 3.142857143.. 1163 1164 my $a = $x->accuracy() || 0; 1165 if ($a != 0 || !$MBI->_is_one($x->{_d})) 1166 { 1167 # n/d 1168 return Math::BigFloat->new($x->{sign} . $MBI->_str($x->{_n}))->bdiv( $MBI->_str($x->{_d}), $x->accuracy()); 1169 } 1170 # just n 1171 Math::BigFloat->new($x->{sign} . $MBI->_str($x->{_n})); 1172 } 1173 1174 sub broot 1175 { 1176 # set up parameters 1177 my ($self,$x,$y,@r) = (ref($_[0]),@_); 1178 # objectify is costly, so avoid it 1179 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1180 { 1181 ($self,$x,$y,@r) = objectify(2,@_); 1182 } 1183 1184 if ($x->is_int() && $y->is_int()) 1185 { 1186 return $self->new($x->as_number()->broot($y->as_number(),@r)); 1187 } 1188 1189 # do it with floats 1190 $x->_new_from_float( $x->_as_float()->broot($y,@r) ); 1191 } 1192 1193 sub bmodpow 1194 { 1195 # set up parameters 1196 my ($self,$x,$y,$m,@r) = (ref($_[0]),@_); 1197 # objectify is costly, so avoid it 1198 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1199 { 1200 ($self,$x,$y,$m,@r) = objectify(3,@_); 1201 } 1202 1203 # $x or $y or $m are NaN or +-inf => NaN 1204 return $x->bnan() 1205 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/ || 1206 $m->{sign} !~ /^[+-]$/; 1207 1208 if ($x->is_int() && $y->is_int() && $m->is_int()) 1209 { 1210 return $self->new($x->as_number()->bmodpow($y->as_number(),$m,@r)); 1211 } 1212 1213 warn ("bmodpow() not fully implemented"); 1214 $x->bnan(); 1215 } 1216 1217 sub bmodinv 1218 { 1219 # set up parameters 1220 my ($self,$x,$y,@r) = (ref($_[0]),@_); 1221 # objectify is costly, so avoid it 1222 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1223 { 1224 ($self,$x,$y,@r) = objectify(2,@_); 1225 } 1226 1227 # $x or $y are NaN or +-inf => NaN 1228 return $x->bnan() 1229 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/; 1230 1231 if ($x->is_int() && $y->is_int()) 1232 { 1233 return $self->new($x->as_number()->bmodinv($y->as_number(),@r)); 1234 } 1235 1236 warn ("bmodinv() not fully implemented"); 1237 $x->bnan(); 1238 } 1239 1240 sub bsqrt 1241 { 1242 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_); 1243 1244 return $x->bnan() if $x->{sign} !~ /^[+]/; # NaN, -inf or < 0 1245 return $x if $x->{sign} eq '+inf'; # sqrt(inf) == inf 1246 return $x->round(@r) if $x->is_zero() || $x->is_one(); 1247 1248 local $Math::BigFloat::upgrade = undef; 1249 local $Math::BigFloat::downgrade = undef; 1250 local $Math::BigFloat::precision = undef; 1251 local $Math::BigFloat::accuracy = undef; 1252 local $Math::BigInt::upgrade = undef; 1253 local $Math::BigInt::precision = undef; 1254 local $Math::BigInt::accuracy = undef; 1255 1256 $x->{_n} = _float_from_part( $x->{_n} )->bsqrt(); 1257 $x->{_d} = _float_from_part( $x->{_d} )->bsqrt(); 1258 1259 # XXX TODO: we probably can optimze this: 1260 1261 # if sqrt(D) was not integer 1262 if ($x->{_d}->{_es} ne '+') 1263 { 1264 $x->{_n}->blsft($x->{_d}->exponent()->babs(),10); # 7.1/4.51 => 7.1/45.1 1265 $x->{_d} = $MBI->_copy( $x->{_d}->{_m} ); # 7.1/45.1 => 71/45.1 1266 } 1267 # if sqrt(N) was not integer 1268 if ($x->{_n}->{_es} ne '+') 1269 { 1270 $x->{_d}->blsft($x->{_n}->exponent()->babs(),10); # 71/45.1 => 710/45.1 1271 $x->{_n} = $MBI->_copy( $x->{_n}->{_m} ); # 710/45.1 => 710/451 1272 } 1273 1274 # convert parts to $MBI again 1275 $x->{_n} = $MBI->_lsft( $MBI->_copy( $x->{_n}->{_m} ), $x->{_n}->{_e}, 10) 1276 if ref($x->{_n}) ne $MBI && ref($x->{_n}) ne 'ARRAY'; 1277 $x->{_d} = $MBI->_lsft( $MBI->_copy( $x->{_d}->{_m} ), $x->{_d}->{_e}, 10) 1278 if ref($x->{_d}) ne $MBI && ref($x->{_d}) ne 'ARRAY'; 1279 1280 $x->bnorm()->round(@r); 1281 } 1282 1283 sub blsft 1284 { 1285 my ($self,$x,$y,$b,@r) = objectify(3,@_); 1286 1287 $b = 2 unless defined $b; 1288 $b = $self->new($b) unless ref ($b); 1289 $x->bmul( $b->copy()->bpow($y), @r); 1290 $x; 1291 } 1292 1293 sub brsft 1294 { 1295 my ($self,$x,$y,$b,@r) = objectify(3,@_); 1296 1297 $b = 2 unless defined $b; 1298 $b = $self->new($b) unless ref ($b); 1299 $x->bdiv( $b->copy()->bpow($y), @r); 1300 $x; 1301 } 1302 1303 ############################################################################## 1304 # round 1305 1306 sub round 1307 { 1308 $_[0]; 1309 } 1310 1311 sub bround 1312 { 1313 $_[0]; 1314 } 1315 1316 sub bfround 1317 { 1318 $_[0]; 1319 } 1320 1321 ############################################################################## 1322 # comparing 1323 1324 sub bcmp 1325 { 1326 # compare two signed numbers 1327 1328 # set up parameters 1329 my ($self,$x,$y) = (ref($_[0]),@_); 1330 # objectify is costly, so avoid it 1331 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1332 { 1333 ($self,$x,$y) = objectify(2,@_); 1334 } 1335 1336 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/)) 1337 { 1338 # handle +-inf and NaN 1339 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan)); 1340 return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/; 1341 return +1 if $x->{sign} eq '+inf'; 1342 return -1 if $x->{sign} eq '-inf'; 1343 return -1 if $y->{sign} eq '+inf'; 1344 return +1; 1345 } 1346 # check sign for speed first 1347 return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y 1348 return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0 1349 1350 # shortcut 1351 my $xz = $MBI->_is_zero($x->{_n}); 1352 my $yz = $MBI->_is_zero($y->{_n}); 1353 return 0 if $xz && $yz; # 0 <=> 0 1354 return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y 1355 return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0 1356 1357 my $t = $MBI->_mul( $MBI->_copy($x->{_n}), $y->{_d}); 1358 my $u = $MBI->_mul( $MBI->_copy($y->{_n}), $x->{_d}); 1359 1360 my $cmp = $MBI->_acmp($t,$u); # signs are equal 1361 $cmp = -$cmp if $x->{sign} eq '-'; # both are '-' => reverse 1362 $cmp; 1363 } 1364 1365 sub bacmp 1366 { 1367 # compare two numbers (as unsigned) 1368 1369 # set up parameters 1370 my ($self,$x,$y) = (ref($_[0]),@_); 1371 # objectify is costly, so avoid it 1372 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) 1373 { 1374 ($self,$x,$y) = objectify(2,$class,@_); 1375 } 1376 1377 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/)) 1378 { 1379 # handle +-inf and NaN 1380 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan)); 1381 return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/; 1382 return 1 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/; 1383 return -1; 1384 } 1385 1386 my $t = $MBI->_mul( $MBI->_copy($x->{_n}), $y->{_d}); 1387 my $u = $MBI->_mul( $MBI->_copy($y->{_n}), $x->{_d}); 1388 $MBI->_acmp($t,$u); # ignore signs 1389 } 1390 1391 ############################################################################## 1392 # output conversation 1393 1394 sub numify 1395 { 1396 # convert 17/8 => float (aka 2.125) 1397 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1398 1399 return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, NaN, etc 1400 1401 # N/1 => N 1402 my $neg = ''; $neg = '-' if $x->{sign} eq '-'; 1403 return $neg . $MBI->_num($x->{_n}) if $MBI->_is_one($x->{_d}); 1404 1405 $x->_as_float()->numify() + 0.0; 1406 } 1407 1408 sub as_number 1409 { 1410 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1411 1412 # NaN, inf etc 1413 return Math::BigInt->new($x->{sign}) if $x->{sign} !~ /^[+-]$/; 1414 1415 my $u = Math::BigInt->bzero(); 1416 $u->{sign} = $x->{sign}; 1417 $u->{value} = $MBI->_div( $MBI->_copy($x->{_n}), $x->{_d}); # 22/7 => 3 1418 $u; 1419 } 1420 1421 sub as_bin 1422 { 1423 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1424 1425 return $x unless $x->is_int(); 1426 1427 my $s = $x->{sign}; $s = '' if $s eq '+'; 1428 $s . $MBI->_as_bin($x->{_n}); 1429 } 1430 1431 sub as_hex 1432 { 1433 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1434 1435 return $x unless $x->is_int(); 1436 1437 my $s = $x->{sign}; $s = '' if $s eq '+'; 1438 $s . $MBI->_as_hex($x->{_n}); 1439 } 1440 1441 sub as_oct 1442 { 1443 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_); 1444 1445 return $x unless $x->is_int(); 1446 1447 my $s = $x->{sign}; $s = '' if $s eq '+'; 1448 $s . $MBI->_as_oct($x->{_n}); 1449 } 1450 1451 ############################################################################## 1452 1453 sub from_hex 1454 { 1455 my $class = shift; 1456 1457 $class->new(@_); 1458 } 1459 1460 sub from_bin 1461 { 1462 my $class = shift; 1463 1464 $class->new(@_); 1465 } 1466 1467 sub from_oct 1468 { 1469 my $class = shift; 1470 1471 my @parts; 1472 for my $c (@_) 1473 { 1474 push @parts, Math::BigInt->from_oct($c); 1475 } 1476 $class->new ( @parts ); 1477 } 1478 1479 ############################################################################## 1480 # import 1481 1482 sub import 1483 { 1484 my $self = shift; 1485 my $l = scalar @_; 1486 my $lib = ''; my @a; 1487 my $try = 'try'; 1488 1489 for ( my $i = 0; $i < $l ; $i++) 1490 { 1491 if ( $_[$i] eq ':constant' ) 1492 { 1493 # this rest causes overlord er load to step in 1494 overload::constant float => sub { $self->new(shift); }; 1495 } 1496 # elsif ($_[$i] eq 'upgrade') 1497 # { 1498 # # this causes upgrading 1499 # $upgrade = $_[$i+1]; # or undef to disable 1500 # $i++; 1501 # } 1502 elsif ($_[$i] eq 'downgrade') 1503 { 1504 # this causes downgrading 1505 $downgrade = $_[$i+1]; # or undef to disable 1506 $i++; 1507 } 1508 elsif ($_[$i] =~ /^(lib|try|only)\z/) 1509 { 1510 $lib = $_[$i+1] || ''; # default Calc 1511 $try = $1; # lib, try or only 1512 $i++; 1513 } 1514 elsif ($_[$i] eq 'with') 1515 { 1516 # this argument is no longer used 1517 #$MBI = $_[$i+1] || 'Math::BigInt::Calc'; # default Math::BigInt::Calc 1518 $i++; 1519 } 1520 else 1521 { 1522 push @a, $_[$i]; 1523 } 1524 } 1525 require Math::BigInt; 1526 1527 # let use Math::BigInt lib => 'GMP'; use Math::BigRat; still have GMP 1528 if ($lib ne '') 1529 { 1530 my @c = split /\s*,\s*/, $lib; 1531 foreach (@c) 1532 { 1533 $_ =~ tr/a-zA-Z0-9://cd; # limit to sane characters 1534 } 1535 $lib = join(",", @c); 1536 } 1537 my @import = ('objectify'); 1538 push @import, $try => $lib if $lib ne ''; 1539 1540 # MBI already loaded, so feed it our lib arguments 1541 Math::BigInt->import( @import ); 1542 1543 $MBI = Math::BigFloat->config()->{lib}; 1544 1545 # register us with MBI to get notified of future lib changes 1546 Math::BigInt::_register_callback( $self, sub { $MBI = $_[0]; } ); 1547 1548 # any non :constant stuff is handled by our parent, Exporter (loaded 1549 # by Math::BigFloat, even if @_ is empty, to give it a chance 1550 $self->SUPER::import(@a); # for subclasses 1551 $self->export_to_level(1,$self,@a); # need this, too 1552 } 1553 1554 1; 1555 1556 __END__ 1557 1558 =head1 NAME 1559 1560 Math::BigRat - Arbitrary big rational numbers 1561 1562 =head1 SYNOPSIS 1563 1564 use Math::BigRat; 1565 1566 my $x = Math::BigRat->new('3/7'); $x += '5/9'; 1567 1568 print $x->bstr(),"\n"; 1569 print $x ** 2,"\n"; 1570 1571 my $y = Math::BigRat->new('inf'); 1572 print "$y ", ($y->is_inf ? 'is' : 'is not') , " infinity\n"; 1573 1574 my $z = Math::BigRat->new(144); $z->bsqrt(); 1575 1576 =head1 DESCRIPTION 1577 1578 Math::BigRat complements Math::BigInt and Math::BigFloat by providing support 1579 for arbitrary big rational numbers. 1580 1581 =head2 MATH LIBRARY 1582 1583 You can change the underlying module that does the low-level 1584 math operations by using: 1585 1586 use Math::BigRat try => 'GMP'; 1587 1588 Note: This needs Math::BigInt::GMP installed. 1589 1590 The following would first try to find Math::BigInt::Foo, then 1591 Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc: 1592 1593 use Math::BigRat try => 'Foo,Math::BigInt::Bar'; 1594 1595 If you want to get warned when the fallback occurs, replace "try" with 1596 "lib": 1597 1598 use Math::BigRat lib => 'Foo,Math::BigInt::Bar'; 1599 1600 If you want the code to die instead, replace "try" with 1601 "only": 1602 1603 use Math::BigRat only => 'Foo,Math::BigInt::Bar'; 1604 1605 =head1 METHODS 1606 1607 Any methods not listed here are derived from Math::BigFloat (or 1608 Math::BigInt), so make sure you check these two modules for further 1609 information. 1610 1611 =head2 new() 1612 1613 $x = Math::BigRat->new('1/3'); 1614 1615 Create a new Math::BigRat object. Input can come in various forms: 1616 1617 $x = Math::BigRat->new(123); # scalars 1618 $x = Math::BigRat->new('inf'); # infinity 1619 $x = Math::BigRat->new('123.3'); # float 1620 $x = Math::BigRat->new('1/3'); # simple string 1621 $x = Math::BigRat->new('1 / 3'); # spaced 1622 $x = Math::BigRat->new('1 / 0.1'); # w/ floats 1623 $x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt 1624 $x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat 1625 $x = Math::BigRat->new(Math::BigInt::Lite->new('2')); # BigLite 1626 1627 # You can also give D and N as different objects: 1628 $x = Math::BigRat->new( 1629 Math::BigInt->new(-123), 1630 Math::BigInt->new(7), 1631 ); # => -123/7 1632 1633 =head2 numerator() 1634 1635 $n = $x->numerator(); 1636 1637 Returns a copy of the numerator (the part above the line) as signed BigInt. 1638 1639 =head2 denominator() 1640 1641 $d = $x->denominator(); 1642 1643 Returns a copy of the denominator (the part under the line) as positive BigInt. 1644 1645 =head2 parts() 1646 1647 ($n,$d) = $x->parts(); 1648 1649 Return a list consisting of (signed) numerator and (unsigned) denominator as 1650 BigInts. 1651 1652 =head2 numify() 1653 1654 my $y = $x->numify(); 1655 1656 Returns the object as a scalar. This will lose some data if the object 1657 cannot be represented by a normal Perl scalar (integer or float), so 1658 use as_int() instead. 1659 1660 This routine is automatically used whenever a scalar is required: 1661 1662 my $x = Math::BigRat->new('3/1'); 1663 @array = (1,2,3); 1664 $y = $array[$x]; # set $y to 3 1665 1666 =head2 as_int()/as_number() 1667 1668 $x = Math::BigRat->new('13/7'); 1669 print $x->as_int(),"\n"; # '1' 1670 1671 Returns a copy of the object as BigInt, truncated to an integer. 1672 1673 C<as_number()> is an alias for C<as_int()>. 1674 1675 =head2 as_hex() 1676 1677 $x = Math::BigRat->new('13'); 1678 print $x->as_hex(),"\n"; # '0xd' 1679 1680 Returns the BigRat as hexadecimal string. Works only for integers. 1681 1682 =head2 as_bin() 1683 1684 $x = Math::BigRat->new('13'); 1685 print $x->as_bin(),"\n"; # '0x1101' 1686 1687 Returns the BigRat as binary string. Works only for integers. 1688 1689 =head2 as_oct() 1690 1691 $x = Math::BigRat->new('13'); 1692 print $x->as_oct(),"\n"; # '015' 1693 1694 Returns the BigRat as octal string. Works only for integers. 1695 1696 =head2 from_hex()/from_bin()/from_oct() 1697 1698 my $h = Math::BigRat->from_hex('0x10'); 1699 my $b = Math::BigRat->from_bin('0b10000000'); 1700 my $o = Math::BigRat->from_oct('020'); 1701 1702 Create a BigRat from an hexadecimal, binary or octal number 1703 in string form. 1704 1705 =head2 length() 1706 1707 $len = $x->length(); 1708 1709 Return the length of $x in digitis for integer values. 1710 1711 =head2 digit() 1712 1713 print Math::BigRat->new('123/1')->digit(1); # 1 1714 print Math::BigRat->new('123/1')->digit(-1); # 3 1715 1716 Return the N'ths digit from X when X is an integer value. 1717 1718 =head2 bnorm() 1719 1720 $x->bnorm(); 1721 1722 Reduce the number to the shortest form. This routine is called 1723 automatically whenever it is needed. 1724 1725 =head2 bfac() 1726 1727 $x->bfac(); 1728 1729 Calculates the factorial of $x. For instance: 1730 1731 print Math::BigRat->new('3/1')->bfac(),"\n"; # 1*2*3 1732 print Math::BigRat->new('5/1')->bfac(),"\n"; # 1*2*3*4*5 1733 1734 Works currently only for integers. 1735 1736 =head2 bround()/round()/bfround() 1737 1738 Are not yet implemented. 1739 1740 =head2 bmod() 1741 1742 use Math::BigRat; 1743 my $x = Math::BigRat->new('7/4'); 1744 my $y = Math::BigRat->new('4/3'); 1745 print $x->bmod($y); 1746 1747 Set $x to the remainder of the division of $x by $y. 1748 1749 =head2 bneg() 1750 1751 $x->bneg(); 1752 1753 Used to negate the object in-place. 1754 1755 =head2 is_one() 1756 1757 print "$x is 1\n" if $x->is_one(); 1758 1759 Return true if $x is exactly one, otherwise false. 1760 1761 =head2 is_zero() 1762 1763 print "$x is 0\n" if $x->is_zero(); 1764 1765 Return true if $x is exactly zero, otherwise false. 1766 1767 =head2 is_pos()/is_positive() 1768 1769 print "$x is >= 0\n" if $x->is_positive(); 1770 1771 Return true if $x is positive (greater than or equal to zero), otherwise 1772 false. Please note that '+inf' is also positive, while 'NaN' and '-inf' aren't. 1773 1774 C<is_positive()> is an alias for C<is_pos()>. 1775 1776 =head2 is_neg()/is_negative() 1777 1778 print "$x is < 0\n" if $x->is_negative(); 1779 1780 Return true if $x is negative (smaller than zero), otherwise false. Please 1781 note that '-inf' is also negative, while 'NaN' and '+inf' aren't. 1782 1783 C<is_negative()> is an alias for C<is_neg()>. 1784 1785 =head2 is_int() 1786 1787 print "$x is an integer\n" if $x->is_int(); 1788 1789 Return true if $x has a denominator of 1 (e.g. no fraction parts), otherwise 1790 false. Please note that '-inf', 'inf' and 'NaN' aren't integer. 1791 1792 =head2 is_odd() 1793 1794 print "$x is odd\n" if $x->is_odd(); 1795 1796 Return true if $x is odd, otherwise false. 1797 1798 =head2 is_even() 1799 1800 print "$x is even\n" if $x->is_even(); 1801 1802 Return true if $x is even, otherwise false. 1803 1804 =head2 bceil() 1805 1806 $x->bceil(); 1807 1808 Set $x to the next bigger integer value (e.g. truncate the number to integer 1809 and then increment it by one). 1810 1811 =head2 bfloor() 1812 1813 $x->bfloor(); 1814 1815 Truncate $x to an integer value. 1816 1817 =head2 bsqrt() 1818 1819 $x->bsqrt(); 1820 1821 Calculate the square root of $x. 1822 1823 =head2 broot() 1824 1825 $x->broot($n); 1826 1827 Calculate the N'th root of $x. 1828 1829 =head2 badd()/bmul()/bsub()/bdiv()/bdec()/binc() 1830 1831 Please see the documentation in L<Math::BigInt>. 1832 1833 =head2 copy() 1834 1835 my $z = $x->copy(); 1836 1837 Makes a deep copy of the object. 1838 1839 Please see the documentation in L<Math::BigInt> for further details. 1840 1841 =head2 bstr()/bsstr() 1842 1843 my $x = Math::BigInt->new('8/4'); 1844 print $x->bstr(),"\n"; # prints 1/2 1845 print $x->bsstr(),"\n"; # prints 1/2 1846 1847 Return a string representating this object. 1848 1849 =head2 bacmp()/bcmp() 1850 1851 Used to compare numbers. 1852 1853 Please see the documentation in L<Math::BigInt> for further details. 1854 1855 =head2 blsft()/brsft() 1856 1857 Used to shift numbers left/right. 1858 1859 Please see the documentation in L<Math::BigInt> for further details. 1860 1861 =head2 bpow() 1862 1863 $x->bpow($y); 1864 1865 Compute $x ** $y. 1866 1867 Please see the documentation in L<Math::BigInt> for further details. 1868 1869 =head2 bexp() 1870 1871 $x->bexp($accuracy); # calculate e ** X 1872 1873 Calculates two integers A and B so that A/B is equal to C<e ** $x>, where C<e> is 1874 Euler's number. 1875 1876 This method was added in v0.20 of Math::BigRat (May 2007). 1877 1878 See also L<blog()>. 1879 1880 =head2 bnok() 1881 1882 $x->bnok($y); # x over y (binomial coefficient n over k) 1883 1884 Calculates the binomial coefficient n over k, also called the "choose" 1885 function. The result is equivalent to: 1886 1887 ( n ) n! 1888 | - | = ------- 1889 ( k ) k!(n-k)! 1890 1891 This method was added in v0.20 of Math::BigRat (May 2007). 1892 1893 =head2 config() 1894 1895 use Data::Dumper; 1896 1897 print Dumper ( Math::BigRat->config() ); 1898 print Math::BigRat->config()->{lib},"\n"; 1899 1900 Returns a hash containing the configuration, e.g. the version number, lib 1901 loaded etc. The following hash keys are currently filled in with the 1902 appropriate information. 1903 1904 key RO/RW Description 1905 Example 1906 ============================================================ 1907 lib RO Name of the Math library 1908 Math::BigInt::Calc 1909 lib_version RO Version of 'lib' 1910 0.30 1911 class RO The class of config you just called 1912 Math::BigRat 1913 version RO version number of the class you used 1914 0.10 1915 upgrade RW To which class numbers are upgraded 1916 undef 1917 downgrade RW To which class numbers are downgraded 1918 undef 1919 precision RW Global precision 1920 undef 1921 accuracy RW Global accuracy 1922 undef 1923 round_mode RW Global round mode 1924 even 1925 div_scale RW Fallback accuracy for div 1926 40 1927 trap_nan RW Trap creation of NaN (undef = no) 1928 undef 1929 trap_inf RW Trap creation of +inf/-inf (undef = no) 1930 undef 1931 1932 By passing a reference to a hash you may set the configuration values. This 1933 works only for values that a marked with a C<RW> above, anything else is 1934 read-only. 1935 1936 =head1 BUGS 1937 1938 Some things are not yet implemented, or only implemented half-way: 1939 1940 =over 2 1941 1942 =item inf handling (partial) 1943 1944 =item NaN handling (partial) 1945 1946 =item rounding (not implemented except for bceil/bfloor) 1947 1948 =item $x ** $y where $y is not an integer 1949 1950 =item bmod(), blog(), bmodinv() and bmodpow() (partial) 1951 1952 =back 1953 1954 =head1 LICENSE 1955 1956 This program is free software; you may redistribute it and/or modify it under 1957 the same terms as Perl itself. 1958 1959 =head1 SEE ALSO 1960 1961 L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>, 1962 L<Math::BigInt::Pari> and L<Math::BigInt::GMP>. 1963 1964 See L<http://search.cpan.org/search?dist=bignum> for a way to use 1965 Math::BigRat. 1966 1967 The package at L<http://search.cpan.org/search?dist=Math%3A%3ABigRat> 1968 may contain more documentation and examples as well as testcases. 1969 1970 =head1 AUTHORS 1971 1972 (C) by Tels L<http://bloodgate.com/> 2001 - 2007. 1973 1974 =cut
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |