[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 package Math::BigInt::Calc; 2 3 use 5.006; 4 use strict; 5 # use warnings; # dont use warnings for older Perls 6 7 our $VERSION = '0.52'; 8 9 # Package to store unsigned big integers in decimal and do math with them 10 11 # Internally the numbers are stored in an array with at least 1 element, no 12 # leading zero parts (except the first) and in base 1eX where X is determined 13 # automatically at loading time to be the maximum possible value 14 15 # todo: 16 # - fully remove funky $# stuff in div() (maybe - that code scares me...) 17 18 # USE_MUL: due to problems on certain os (os390, posix-bc) "* 1e-5" is used 19 # instead of "/ 1e5" at some places, (marked with USE_MUL). Other platforms 20 # BS2000, some Crays need USE_DIV instead. 21 # The BEGIN block is used to determine which of the two variants gives the 22 # correct result. 23 24 # Beware of things like: 25 # $i = $i * $y + $car; $car = int($i / $BASE); $i = $i % $BASE; 26 # This works on x86, but fails on ARM (SA1100, iPAQ) due to whoknows what 27 # reasons. So, use this instead (slower, but correct): 28 # $i = $i * $y + $car; $car = int($i / $BASE); $i -= $BASE * $car; 29 30 ############################################################################## 31 # global constants, flags and accessory 32 33 # announce that we are compatible with MBI v1.83 and up 34 sub api_version () { 2; } 35 36 # constants for easier life 37 my ($BASE,$BASE_LEN,$RBASE,$MAX_VAL); 38 my ($AND_BITS,$XOR_BITS,$OR_BITS); 39 my ($AND_MASK,$XOR_MASK,$OR_MASK); 40 41 sub _base_len 42 { 43 # Set/get the BASE_LEN and assorted other, connected values. 44 # Used only by the testsuite, the set variant is used only by the BEGIN 45 # block below: 46 shift; 47 48 my ($b, $int) = @_; 49 if (defined $b) 50 { 51 # avoid redefinitions 52 undef &_mul; 53 undef &_div; 54 55 if ($] >= 5.008 && $int && $b > 7) 56 { 57 $BASE_LEN = $b; 58 *_mul = \&_mul_use_div_64; 59 *_div = \&_div_use_div_64; 60 $BASE = int("1e".$BASE_LEN); 61 $MAX_VAL = $BASE-1; 62 return $BASE_LEN unless wantarray; 63 return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL, $BASE); 64 } 65 66 # find whether we can use mul or div in mul()/div() 67 $BASE_LEN = $b+1; 68 my $caught = 0; 69 while (--$BASE_LEN > 5) 70 { 71 $BASE = int("1e".$BASE_LEN); 72 $RBASE = abs('1e-'.$BASE_LEN); # see USE_MUL 73 $caught = 0; 74 $caught += 1 if (int($BASE * $RBASE) != 1); # should be 1 75 $caught += 2 if (int($BASE / $BASE) != 1); # should be 1 76 last if $caught != 3; 77 } 78 $BASE = int("1e".$BASE_LEN); 79 $RBASE = abs('1e-'.$BASE_LEN); # see USE_MUL 80 $MAX_VAL = $BASE-1; 81 82 # ($caught & 1) != 0 => cannot use MUL 83 # ($caught & 2) != 0 => cannot use DIV 84 if ($caught == 2) # 2 85 { 86 # must USE_MUL since we cannot use DIV 87 *_mul = \&_mul_use_mul; 88 *_div = \&_div_use_mul; 89 } 90 else # 0 or 1 91 { 92 # can USE_DIV instead 93 *_mul = \&_mul_use_div; 94 *_div = \&_div_use_div; 95 } 96 } 97 return $BASE_LEN unless wantarray; 98 return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL, $BASE); 99 } 100 101 sub _new 102 { 103 # (ref to string) return ref to num_array 104 # Convert a number from string format (without sign) to internal base 105 # 1ex format. Assumes normalized value as input. 106 my $il = length($_[1])-1; 107 108 # < BASE_LEN due len-1 above 109 return [ int($_[1]) ] if $il < $BASE_LEN; # shortcut for short numbers 110 111 # this leaves '00000' instead of int 0 and will be corrected after any op 112 [ reverse(unpack("a" . ($il % $BASE_LEN+1) 113 . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ]; 114 } 115 116 BEGIN 117 { 118 # from Daniel Pfeiffer: determine largest group of digits that is precisely 119 # multipliable with itself plus carry 120 # Test now changed to expect the proper pattern, not a result off by 1 or 2 121 my ($e, $num) = 3; # lowest value we will use is 3+1-1 = 3 122 do 123 { 124 $num = ('9' x ++$e) + 0; 125 $num *= $num + 1.0; 126 } while ("$num" =~ /9{$e}0{$e}/); # must be a certain pattern 127 $e--; # last test failed, so retract one step 128 # the limits below brush the problems with the test above under the rug: 129 # the test should be able to find the proper $e automatically 130 $e = 5 if $^O =~ /^uts/; # UTS get's some special treatment 131 $e = 5 if $^O =~ /^unicos/; # unicos is also problematic (6 seems to work 132 # there, but we play safe) 133 134 my $int = 0; 135 if ($e > 7) 136 { 137 use integer; 138 my $e1 = 7; 139 $num = 7; 140 do 141 { 142 $num = ('9' x ++$e1) + 0; 143 $num *= $num + 1; 144 } while ("$num" =~ /9{$e1}0{$e1}/); # must be a certain pattern 145 $e1--; # last test failed, so retract one step 146 if ($e1 > 7) 147 { 148 $int = 1; $e = $e1; 149 } 150 } 151 152 __PACKAGE__->_base_len($e,$int); # set and store 153 154 use integer; 155 # find out how many bits _and, _or and _xor can take (old default = 16) 156 # I don't think anybody has yet 128 bit scalars, so let's play safe. 157 local $^W = 0; # don't warn about 'nonportable number' 158 $AND_BITS = 15; $XOR_BITS = 15; $OR_BITS = 15; 159 160 # find max bits, we will not go higher than numberofbits that fit into $BASE 161 # to make _and etc simpler (and faster for smaller, slower for large numbers) 162 my $max = 16; 163 while (2 ** $max < $BASE) { $max++; } 164 { 165 no integer; 166 $max = 16 if $] < 5.006; # older Perls might not take >16 too well 167 } 168 my ($x,$y,$z); 169 do { 170 $AND_BITS++; 171 $x = CORE::oct('0b' . '1' x $AND_BITS); $y = $x & $x; 172 $z = (2 ** $AND_BITS) - 1; 173 } while ($AND_BITS < $max && $x == $z && $y == $x); 174 $AND_BITS --; # retreat one step 175 do { 176 $XOR_BITS++; 177 $x = CORE::oct('0b' . '1' x $XOR_BITS); $y = $x ^ 0; 178 $z = (2 ** $XOR_BITS) - 1; 179 } while ($XOR_BITS < $max && $x == $z && $y == $x); 180 $XOR_BITS --; # retreat one step 181 do { 182 $OR_BITS++; 183 $x = CORE::oct('0b' . '1' x $OR_BITS); $y = $x | $x; 184 $z = (2 ** $OR_BITS) - 1; 185 } while ($OR_BITS < $max && $x == $z && $y == $x); 186 $OR_BITS --; # retreat one step 187 188 $AND_MASK = __PACKAGE__->_new( ( 2 ** $AND_BITS )); 189 $XOR_MASK = __PACKAGE__->_new( ( 2 ** $XOR_BITS )); 190 $OR_MASK = __PACKAGE__->_new( ( 2 ** $OR_BITS )); 191 192 # We can compute the approximate lenght no faster than the real length: 193 *_alen = \&_len; 194 } 195 196 ############################################################################### 197 198 sub _zero 199 { 200 # create a zero 201 [ 0 ]; 202 } 203 204 sub _one 205 { 206 # create a one 207 [ 1 ]; 208 } 209 210 sub _two 211 { 212 # create a two (used internally for shifting) 213 [ 2 ]; 214 } 215 216 sub _ten 217 { 218 # create a 10 (used internally for shifting) 219 [ 10 ]; 220 } 221 222 sub _1ex 223 { 224 # create a 1Ex 225 my $rem = $_[1] % $BASE_LEN; # remainder 226 my $parts = $_[1] / $BASE_LEN; # parts 227 228 # 000000, 000000, 100 229 [ (0) x $parts, '1' . ('0' x $rem) ]; 230 } 231 232 sub _copy 233 { 234 # make a true copy 235 [ @{$_[1]} ]; 236 } 237 238 # catch and throw away 239 sub import { } 240 241 ############################################################################## 242 # convert back to string and number 243 244 sub _str 245 { 246 # (ref to BINT) return num_str 247 # Convert number from internal base 100000 format to string format. 248 # internal format is always normalized (no leading zeros, "-0" => "+0") 249 my $ar = $_[1]; 250 251 my $l = scalar @$ar; # number of parts 252 if ($l < 1) # should not happen 253 { 254 require Carp; 255 Carp::croak("$_[1] has no elements"); 256 } 257 258 my $ret = ""; 259 # handle first one different to strip leading zeros from it (there are no 260 # leading zero parts in internal representation) 261 $l --; $ret .= int($ar->[$l]); $l--; 262 # Interestingly, the pre-padd method uses more time 263 # the old grep variant takes longer (14 vs. 10 sec) 264 my $z = '0' x ($BASE_LEN-1); 265 while ($l >= 0) 266 { 267 $ret .= substr($z.$ar->[$l],-$BASE_LEN); # fastest way I could think of 268 $l--; 269 } 270 $ret; 271 } 272 273 sub _num 274 { 275 # Make a number (scalar int/float) from a BigInt object 276 my $x = $_[1]; 277 278 return 0+$x->[0] if scalar @$x == 1; # below $BASE 279 my $fac = 1; 280 my $num = 0; 281 foreach (@$x) 282 { 283 $num += $fac*$_; $fac *= $BASE; 284 } 285 $num; 286 } 287 288 ############################################################################## 289 # actual math code 290 291 sub _add 292 { 293 # (ref to int_num_array, ref to int_num_array) 294 # routine to add two base 1eX numbers 295 # stolen from Knuth Vol 2 Algorithm A pg 231 296 # there are separate routines to add and sub as per Knuth pg 233 297 # This routine clobbers up array x, but not y. 298 299 my ($c,$x,$y) = @_; 300 301 return $x if (@$y == 1) && $y->[0] == 0; # $x + 0 => $x 302 if ((@$x == 1) && $x->[0] == 0) # 0 + $y => $y->copy 303 { 304 # twice as slow as $x = [ @$y ], but nec. to retain $x as ref :( 305 @$x = @$y; return $x; 306 } 307 308 # for each in Y, add Y to X and carry. If after that, something is left in 309 # X, foreach in X add carry to X and then return X, carry 310 # Trades one "$j++" for having to shift arrays 311 my $i; my $car = 0; my $j = 0; 312 for $i (@$y) 313 { 314 $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0; 315 $j++; 316 } 317 while ($car != 0) 318 { 319 $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0; $j++; 320 } 321 $x; 322 } 323 324 sub _inc 325 { 326 # (ref to int_num_array, ref to int_num_array) 327 # Add 1 to $x, modify $x in place 328 my ($c,$x) = @_; 329 330 for my $i (@$x) 331 { 332 return $x if (($i += 1) < $BASE); # early out 333 $i = 0; # overflow, next 334 } 335 push @$x,1 if (($x->[-1] || 0) == 0); # last overflowed, so extend 336 $x; 337 } 338 339 sub _dec 340 { 341 # (ref to int_num_array, ref to int_num_array) 342 # Sub 1 from $x, modify $x in place 343 my ($c,$x) = @_; 344 345 my $MAX = $BASE-1; # since MAX_VAL based on BASE 346 for my $i (@$x) 347 { 348 last if (($i -= 1) >= 0); # early out 349 $i = $MAX; # underflow, next 350 } 351 pop @$x if $x->[-1] == 0 && @$x > 1; # last underflowed (but leave 0) 352 $x; 353 } 354 355 sub _sub 356 { 357 # (ref to int_num_array, ref to int_num_array, swap) 358 # subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y 359 # subtract Y from X by modifying x in place 360 my ($c,$sx,$sy,$s) = @_; 361 362 my $car = 0; my $i; my $j = 0; 363 if (!$s) 364 { 365 for $i (@$sx) 366 { 367 last unless defined $sy->[$j] || $car; 368 $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0); $j++; 369 } 370 # might leave leading zeros, so fix that 371 return __strip_zeros($sx); 372 } 373 for $i (@$sx) 374 { 375 # we can't do an early out if $x is < than $y, since we 376 # need to copy the high chunks from $y. Found by Bob Mathews. 377 #last unless defined $sy->[$j] || $car; 378 $sy->[$j] += $BASE 379 if $car = (($sy->[$j] = $i-($sy->[$j]||0) - $car) < 0); 380 $j++; 381 } 382 # might leave leading zeros, so fix that 383 __strip_zeros($sy); 384 } 385 386 sub _mul_use_mul 387 { 388 # (ref to int_num_array, ref to int_num_array) 389 # multiply two numbers in internal representation 390 # modifies first arg, second need not be different from first 391 my ($c,$xv,$yv) = @_; 392 393 if (@$yv == 1) 394 { 395 # shortcut for two very short numbers (improved by Nathan Zook) 396 # works also if xv and yv are the same reference, and handles also $x == 0 397 if (@$xv == 1) 398 { 399 if (($xv->[0] *= $yv->[0]) >= $BASE) 400 { 401 $xv->[0] = $xv->[0] - ($xv->[1] = int($xv->[0] * $RBASE)) * $BASE; 402 }; 403 return $xv; 404 } 405 # $x * 0 => 0 406 if ($yv->[0] == 0) 407 { 408 @$xv = (0); 409 return $xv; 410 } 411 # multiply a large number a by a single element one, so speed up 412 my $y = $yv->[0]; my $car = 0; 413 foreach my $i (@$xv) 414 { 415 $i = $i * $y + $car; $car = int($i * $RBASE); $i -= $car * $BASE; 416 } 417 push @$xv, $car if $car != 0; 418 return $xv; 419 } 420 # shortcut for result $x == 0 => result = 0 421 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) ); 422 423 # since multiplying $x with $x fails, make copy in this case 424 $yv = [@$xv] if $xv == $yv; # same references? 425 426 my @prod = (); my ($prod,$car,$cty,$xi,$yi); 427 428 for $xi (@$xv) 429 { 430 $car = 0; $cty = 0; 431 432 # slow variant 433 # for $yi (@$yv) 434 # { 435 # $prod = $xi * $yi + ($prod[$cty] || 0) + $car; 436 # $prod[$cty++] = 437 # $prod - ($car = int($prod * RBASE)) * $BASE; # see USE_MUL 438 # } 439 # $prod[$cty] += $car if $car; # need really to check for 0? 440 # $xi = shift @prod; 441 442 # faster variant 443 # looping through this if $xi == 0 is silly - so optimize it away! 444 $xi = (shift @prod || 0), next if $xi == 0; 445 for $yi (@$yv) 446 { 447 $prod = $xi * $yi + ($prod[$cty] || 0) + $car; 448 ## this is actually a tad slower 449 ## $prod = $prod[$cty]; $prod += ($car + $xi * $yi); # no ||0 here 450 $prod[$cty++] = 451 $prod - ($car = int($prod * $RBASE)) * $BASE; # see USE_MUL 452 } 453 $prod[$cty] += $car if $car; # need really to check for 0? 454 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy 455 } 456 push @$xv, @prod; 457 # can't have leading zeros 458 # __strip_zeros($xv); 459 $xv; 460 } 461 462 sub _mul_use_div_64 463 { 464 # (ref to int_num_array, ref to int_num_array) 465 # multiply two numbers in internal representation 466 # modifies first arg, second need not be different from first 467 # works for 64 bit integer with "use integer" 468 my ($c,$xv,$yv) = @_; 469 470 use integer; 471 if (@$yv == 1) 472 { 473 # shortcut for two small numbers, also handles $x == 0 474 if (@$xv == 1) 475 { 476 # shortcut for two very short numbers (improved by Nathan Zook) 477 # works also if xv and yv are the same reference, and handles also $x == 0 478 if (($xv->[0] *= $yv->[0]) >= $BASE) 479 { 480 $xv->[0] = 481 $xv->[0] - ($xv->[1] = $xv->[0] / $BASE) * $BASE; 482 }; 483 return $xv; 484 } 485 # $x * 0 => 0 486 if ($yv->[0] == 0) 487 { 488 @$xv = (0); 489 return $xv; 490 } 491 # multiply a large number a by a single element one, so speed up 492 my $y = $yv->[0]; my $car = 0; 493 foreach my $i (@$xv) 494 { 495 #$i = $i * $y + $car; $car = $i / $BASE; $i -= $car * $BASE; 496 $i = $i * $y + $car; $i -= ($car = $i / $BASE) * $BASE; 497 } 498 push @$xv, $car if $car != 0; 499 return $xv; 500 } 501 # shortcut for result $x == 0 => result = 0 502 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) ); 503 504 # since multiplying $x with $x fails, make copy in this case 505 $yv = [@$xv] if $xv == $yv; # same references? 506 507 my @prod = (); my ($prod,$car,$cty,$xi,$yi); 508 for $xi (@$xv) 509 { 510 $car = 0; $cty = 0; 511 # looping through this if $xi == 0 is silly - so optimize it away! 512 $xi = (shift @prod || 0), next if $xi == 0; 513 for $yi (@$yv) 514 { 515 $prod = $xi * $yi + ($prod[$cty] || 0) + $car; 516 $prod[$cty++] = $prod - ($car = $prod / $BASE) * $BASE; 517 } 518 $prod[$cty] += $car if $car; # need really to check for 0? 519 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy 520 } 521 push @$xv, @prod; 522 $xv; 523 } 524 525 sub _mul_use_div 526 { 527 # (ref to int_num_array, ref to int_num_array) 528 # multiply two numbers in internal representation 529 # modifies first arg, second need not be different from first 530 my ($c,$xv,$yv) = @_; 531 532 if (@$yv == 1) 533 { 534 # shortcut for two small numbers, also handles $x == 0 535 if (@$xv == 1) 536 { 537 # shortcut for two very short numbers (improved by Nathan Zook) 538 # works also if xv and yv are the same reference, and handles also $x == 0 539 if (($xv->[0] *= $yv->[0]) >= $BASE) 540 { 541 $xv->[0] = 542 $xv->[0] - ($xv->[1] = int($xv->[0] / $BASE)) * $BASE; 543 }; 544 return $xv; 545 } 546 # $x * 0 => 0 547 if ($yv->[0] == 0) 548 { 549 @$xv = (0); 550 return $xv; 551 } 552 # multiply a large number a by a single element one, so speed up 553 my $y = $yv->[0]; my $car = 0; 554 foreach my $i (@$xv) 555 { 556 $i = $i * $y + $car; $car = int($i / $BASE); $i -= $car * $BASE; 557 # This (together with use integer;) does not work on 32-bit Perls 558 #$i = $i * $y + $car; $i -= ($car = $i / $BASE) * $BASE; 559 } 560 push @$xv, $car if $car != 0; 561 return $xv; 562 } 563 # shortcut for result $x == 0 => result = 0 564 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) ); 565 566 # since multiplying $x with $x fails, make copy in this case 567 $yv = [@$xv] if $xv == $yv; # same references? 568 569 my @prod = (); my ($prod,$car,$cty,$xi,$yi); 570 for $xi (@$xv) 571 { 572 $car = 0; $cty = 0; 573 # looping through this if $xi == 0 is silly - so optimize it away! 574 $xi = (shift @prod || 0), next if $xi == 0; 575 for $yi (@$yv) 576 { 577 $prod = $xi * $yi + ($prod[$cty] || 0) + $car; 578 $prod[$cty++] = $prod - ($car = int($prod / $BASE)) * $BASE; 579 } 580 $prod[$cty] += $car if $car; # need really to check for 0? 581 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy 582 } 583 push @$xv, @prod; 584 # can't have leading zeros 585 # __strip_zeros($xv); 586 $xv; 587 } 588 589 sub _div_use_mul 590 { 591 # ref to array, ref to array, modify first array and return remainder if 592 # in list context 593 594 # see comments in _div_use_div() for more explanations 595 596 my ($c,$x,$yorg) = @_; 597 598 # the general div algorithmn here is about O(N*N) and thus quite slow, so 599 # we first check for some special cases and use shortcuts to handle them. 600 601 # This works, because we store the numbers in a chunked format where each 602 # element contains 5..7 digits (depending on system). 603 604 # if both numbers have only one element: 605 if (@$x == 1 && @$yorg == 1) 606 { 607 # shortcut, $yorg and $x are two small numbers 608 if (wantarray) 609 { 610 my $r = [ $x->[0] % $yorg->[0] ]; 611 $x->[0] = int($x->[0] / $yorg->[0]); 612 return ($x,$r); 613 } 614 else 615 { 616 $x->[0] = int($x->[0] / $yorg->[0]); 617 return $x; 618 } 619 } 620 621 # if x has more than one, but y has only one element: 622 if (@$yorg == 1) 623 { 624 my $rem; 625 $rem = _mod($c,[ @$x ],$yorg) if wantarray; 626 627 # shortcut, $y is < $BASE 628 my $j = scalar @$x; my $r = 0; 629 my $y = $yorg->[0]; my $b; 630 while ($j-- > 0) 631 { 632 $b = $r * $BASE + $x->[$j]; 633 $x->[$j] = int($b/$y); 634 $r = $b % $y; 635 } 636 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero 637 return ($x,$rem) if wantarray; 638 return $x; 639 } 640 641 # now x and y have more than one element 642 643 # check whether y has more elements than x, if yet, the result will be 0 644 if (@$yorg > @$x) 645 { 646 my $rem; 647 $rem = [@$x] if wantarray; # make copy 648 splice (@$x,1); # keep ref to original array 649 $x->[0] = 0; # set to 0 650 return ($x,$rem) if wantarray; # including remainder? 651 return $x; # only x, which is [0] now 652 } 653 # check whether the numbers have the same number of elements, in that case 654 # the result will fit into one element and can be computed efficiently 655 if (@$yorg == @$x) 656 { 657 my $rem; 658 # if $yorg has more digits than $x (it's leading element is longer than 659 # the one from $x), the result will also be 0: 660 if (length(int($yorg->[-1])) > length(int($x->[-1]))) 661 { 662 $rem = [@$x] if wantarray; # make copy 663 splice (@$x,1); # keep ref to org array 664 $x->[0] = 0; # set to 0 665 return ($x,$rem) if wantarray; # including remainder? 666 return $x; 667 } 668 # now calculate $x / $yorg 669 if (length(int($yorg->[-1])) == length(int($x->[-1]))) 670 { 671 # same length, so make full compare 672 673 my $a = 0; my $j = scalar @$x - 1; 674 # manual way (abort if unequal, good for early ne) 675 while ($j >= 0) 676 { 677 last if ($a = $x->[$j] - $yorg->[$j]); $j--; 678 } 679 # $a contains the result of the compare between X and Y 680 # a < 0: x < y, a == 0: x == y, a > 0: x > y 681 if ($a <= 0) 682 { 683 $rem = [ 0 ]; # a = 0 => x == y => rem 0 684 $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x 685 splice(@$x,1); # keep single element 686 $x->[0] = 0; # if $a < 0 687 $x->[0] = 1 if $a == 0; # $x == $y 688 return ($x,$rem) if wantarray; 689 return $x; 690 } 691 # $x >= $y, so proceed normally 692 } 693 } 694 695 # all other cases: 696 697 my $y = [ @$yorg ]; # always make copy to preserve 698 699 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0); 700 701 $car = $bar = $prd = 0; 702 if (($dd = int($BASE/($y->[-1]+1))) != 1) 703 { 704 for $xi (@$x) 705 { 706 $xi = $xi * $dd + $car; 707 $xi -= ($car = int($xi * $RBASE)) * $BASE; # see USE_MUL 708 } 709 push(@$x, $car); $car = 0; 710 for $yi (@$y) 711 { 712 $yi = $yi * $dd + $car; 713 $yi -= ($car = int($yi * $RBASE)) * $BASE; # see USE_MUL 714 } 715 } 716 else 717 { 718 push(@$x, 0); 719 } 720 @q = (); ($v2,$v1) = @$y[-2,-1]; 721 $v2 = 0 unless $v2; 722 while ($#$x > $#$y) 723 { 724 ($u2,$u1,$u0) = @$x[-3..-1]; 725 $u2 = 0 unless $u2; 726 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n" 727 # if $v1 == 0; 728 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$BASE+$u1)/$v1)); 729 --$q while ($v2*$q > ($u0*$BASE+$u1-$q*$v1)*$BASE+$u2); 730 if ($q) 731 { 732 ($car, $bar) = (0,0); 733 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) 734 { 735 $prd = $q * $y->[$yi] + $car; 736 $prd -= ($car = int($prd * $RBASE)) * $BASE; # see USE_MUL 737 $x->[$xi] += $BASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0)); 738 } 739 if ($x->[-1] < $car + $bar) 740 { 741 $car = 0; --$q; 742 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) 743 { 744 $x->[$xi] -= $BASE 745 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE)); 746 } 747 } 748 } 749 pop(@$x); 750 unshift(@q, $q); 751 } 752 if (wantarray) 753 { 754 @d = (); 755 if ($dd != 1) 756 { 757 $car = 0; 758 for $xi (reverse @$x) 759 { 760 $prd = $car * $BASE + $xi; 761 $car = $prd - ($tmp = int($prd / $dd)) * $dd; # see USE_MUL 762 unshift(@d, $tmp); 763 } 764 } 765 else 766 { 767 @d = @$x; 768 } 769 @$x = @q; 770 my $d = \@d; 771 __strip_zeros($x); 772 __strip_zeros($d); 773 return ($x,$d); 774 } 775 @$x = @q; 776 __strip_zeros($x); 777 $x; 778 } 779 780 sub _div_use_div_64 781 { 782 # ref to array, ref to array, modify first array and return remainder if 783 # in list context 784 # This version works on 64 bit integers 785 my ($c,$x,$yorg) = @_; 786 787 use integer; 788 # the general div algorithmn here is about O(N*N) and thus quite slow, so 789 # we first check for some special cases and use shortcuts to handle them. 790 791 # This works, because we store the numbers in a chunked format where each 792 # element contains 5..7 digits (depending on system). 793 794 # if both numbers have only one element: 795 if (@$x == 1 && @$yorg == 1) 796 { 797 # shortcut, $yorg and $x are two small numbers 798 if (wantarray) 799 { 800 my $r = [ $x->[0] % $yorg->[0] ]; 801 $x->[0] = int($x->[0] / $yorg->[0]); 802 return ($x,$r); 803 } 804 else 805 { 806 $x->[0] = int($x->[0] / $yorg->[0]); 807 return $x; 808 } 809 } 810 # if x has more than one, but y has only one element: 811 if (@$yorg == 1) 812 { 813 my $rem; 814 $rem = _mod($c,[ @$x ],$yorg) if wantarray; 815 816 # shortcut, $y is < $BASE 817 my $j = scalar @$x; my $r = 0; 818 my $y = $yorg->[0]; my $b; 819 while ($j-- > 0) 820 { 821 $b = $r * $BASE + $x->[$j]; 822 $x->[$j] = int($b/$y); 823 $r = $b % $y; 824 } 825 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero 826 return ($x,$rem) if wantarray; 827 return $x; 828 } 829 # now x and y have more than one element 830 831 # check whether y has more elements than x, if yet, the result will be 0 832 if (@$yorg > @$x) 833 { 834 my $rem; 835 $rem = [@$x] if wantarray; # make copy 836 splice (@$x,1); # keep ref to original array 837 $x->[0] = 0; # set to 0 838 return ($x,$rem) if wantarray; # including remainder? 839 return $x; # only x, which is [0] now 840 } 841 # check whether the numbers have the same number of elements, in that case 842 # the result will fit into one element and can be computed efficiently 843 if (@$yorg == @$x) 844 { 845 my $rem; 846 # if $yorg has more digits than $x (it's leading element is longer than 847 # the one from $x), the result will also be 0: 848 if (length(int($yorg->[-1])) > length(int($x->[-1]))) 849 { 850 $rem = [@$x] if wantarray; # make copy 851 splice (@$x,1); # keep ref to org array 852 $x->[0] = 0; # set to 0 853 return ($x,$rem) if wantarray; # including remainder? 854 return $x; 855 } 856 # now calculate $x / $yorg 857 858 if (length(int($yorg->[-1])) == length(int($x->[-1]))) 859 { 860 # same length, so make full compare 861 862 my $a = 0; my $j = scalar @$x - 1; 863 # manual way (abort if unequal, good for early ne) 864 while ($j >= 0) 865 { 866 last if ($a = $x->[$j] - $yorg->[$j]); $j--; 867 } 868 # $a contains the result of the compare between X and Y 869 # a < 0: x < y, a == 0: x == y, a > 0: x > y 870 if ($a <= 0) 871 { 872 $rem = [ 0 ]; # a = 0 => x == y => rem 0 873 $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x 874 splice(@$x,1); # keep single element 875 $x->[0] = 0; # if $a < 0 876 $x->[0] = 1 if $a == 0; # $x == $y 877 return ($x,$rem) if wantarray; # including remainder? 878 return $x; 879 } 880 # $x >= $y, so proceed normally 881 882 } 883 } 884 885 # all other cases: 886 887 my $y = [ @$yorg ]; # always make copy to preserve 888 889 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0); 890 891 $car = $bar = $prd = 0; 892 if (($dd = int($BASE/($y->[-1]+1))) != 1) 893 { 894 for $xi (@$x) 895 { 896 $xi = $xi * $dd + $car; 897 $xi -= ($car = int($xi / $BASE)) * $BASE; 898 } 899 push(@$x, $car); $car = 0; 900 for $yi (@$y) 901 { 902 $yi = $yi * $dd + $car; 903 $yi -= ($car = int($yi / $BASE)) * $BASE; 904 } 905 } 906 else 907 { 908 push(@$x, 0); 909 } 910 911 # @q will accumulate the final result, $q contains the current computed 912 # part of the final result 913 914 @q = (); ($v2,$v1) = @$y[-2,-1]; 915 $v2 = 0 unless $v2; 916 while ($#$x > $#$y) 917 { 918 ($u2,$u1,$u0) = @$x[-3..-1]; 919 $u2 = 0 unless $u2; 920 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n" 921 # if $v1 == 0; 922 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$BASE+$u1)/$v1)); 923 --$q while ($v2*$q > ($u0*$BASE+$u1-$q*$v1)*$BASE+$u2); 924 if ($q) 925 { 926 ($car, $bar) = (0,0); 927 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) 928 { 929 $prd = $q * $y->[$yi] + $car; 930 $prd -= ($car = int($prd / $BASE)) * $BASE; 931 $x->[$xi] += $BASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0)); 932 } 933 if ($x->[-1] < $car + $bar) 934 { 935 $car = 0; --$q; 936 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) 937 { 938 $x->[$xi] -= $BASE 939 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE)); 940 } 941 } 942 } 943 pop(@$x); unshift(@q, $q); 944 } 945 if (wantarray) 946 { 947 @d = (); 948 if ($dd != 1) 949 { 950 $car = 0; 951 for $xi (reverse @$x) 952 { 953 $prd = $car * $BASE + $xi; 954 $car = $prd - ($tmp = int($prd / $dd)) * $dd; 955 unshift(@d, $tmp); 956 } 957 } 958 else 959 { 960 @d = @$x; 961 } 962 @$x = @q; 963 my $d = \@d; 964 __strip_zeros($x); 965 __strip_zeros($d); 966 return ($x,$d); 967 } 968 @$x = @q; 969 __strip_zeros($x); 970 $x; 971 } 972 973 sub _div_use_div 974 { 975 # ref to array, ref to array, modify first array and return remainder if 976 # in list context 977 my ($c,$x,$yorg) = @_; 978 979 # the general div algorithmn here is about O(N*N) and thus quite slow, so 980 # we first check for some special cases and use shortcuts to handle them. 981 982 # This works, because we store the numbers in a chunked format where each 983 # element contains 5..7 digits (depending on system). 984 985 # if both numbers have only one element: 986 if (@$x == 1 && @$yorg == 1) 987 { 988 # shortcut, $yorg and $x are two small numbers 989 if (wantarray) 990 { 991 my $r = [ $x->[0] % $yorg->[0] ]; 992 $x->[0] = int($x->[0] / $yorg->[0]); 993 return ($x,$r); 994 } 995 else 996 { 997 $x->[0] = int($x->[0] / $yorg->[0]); 998 return $x; 999 } 1000 } 1001 # if x has more than one, but y has only one element: 1002 if (@$yorg == 1) 1003 { 1004 my $rem; 1005 $rem = _mod($c,[ @$x ],$yorg) if wantarray; 1006 1007 # shortcut, $y is < $BASE 1008 my $j = scalar @$x; my $r = 0; 1009 my $y = $yorg->[0]; my $b; 1010 while ($j-- > 0) 1011 { 1012 $b = $r * $BASE + $x->[$j]; 1013 $x->[$j] = int($b/$y); 1014 $r = $b % $y; 1015 } 1016 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero 1017 return ($x,$rem) if wantarray; 1018 return $x; 1019 } 1020 # now x and y have more than one element 1021 1022 # check whether y has more elements than x, if yet, the result will be 0 1023 if (@$yorg > @$x) 1024 { 1025 my $rem; 1026 $rem = [@$x] if wantarray; # make copy 1027 splice (@$x,1); # keep ref to original array 1028 $x->[0] = 0; # set to 0 1029 return ($x,$rem) if wantarray; # including remainder? 1030 return $x; # only x, which is [0] now 1031 } 1032 # check whether the numbers have the same number of elements, in that case 1033 # the result will fit into one element and can be computed efficiently 1034 if (@$yorg == @$x) 1035 { 1036 my $rem; 1037 # if $yorg has more digits than $x (it's leading element is longer than 1038 # the one from $x), the result will also be 0: 1039 if (length(int($yorg->[-1])) > length(int($x->[-1]))) 1040 { 1041 $rem = [@$x] if wantarray; # make copy 1042 splice (@$x,1); # keep ref to org array 1043 $x->[0] = 0; # set to 0 1044 return ($x,$rem) if wantarray; # including remainder? 1045 return $x; 1046 } 1047 # now calculate $x / $yorg 1048 1049 if (length(int($yorg->[-1])) == length(int($x->[-1]))) 1050 { 1051 # same length, so make full compare 1052 1053 my $a = 0; my $j = scalar @$x - 1; 1054 # manual way (abort if unequal, good for early ne) 1055 while ($j >= 0) 1056 { 1057 last if ($a = $x->[$j] - $yorg->[$j]); $j--; 1058 } 1059 # $a contains the result of the compare between X and Y 1060 # a < 0: x < y, a == 0: x == y, a > 0: x > y 1061 if ($a <= 0) 1062 { 1063 $rem = [ 0 ]; # a = 0 => x == y => rem 0 1064 $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x 1065 splice(@$x,1); # keep single element 1066 $x->[0] = 0; # if $a < 0 1067 $x->[0] = 1 if $a == 0; # $x == $y 1068 return ($x,$rem) if wantarray; # including remainder? 1069 return $x; 1070 } 1071 # $x >= $y, so proceed normally 1072 1073 } 1074 } 1075 1076 # all other cases: 1077 1078 my $y = [ @$yorg ]; # always make copy to preserve 1079 1080 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0); 1081 1082 $car = $bar = $prd = 0; 1083 if (($dd = int($BASE/($y->[-1]+1))) != 1) 1084 { 1085 for $xi (@$x) 1086 { 1087 $xi = $xi * $dd + $car; 1088 $xi -= ($car = int($xi / $BASE)) * $BASE; 1089 } 1090 push(@$x, $car); $car = 0; 1091 for $yi (@$y) 1092 { 1093 $yi = $yi * $dd + $car; 1094 $yi -= ($car = int($yi / $BASE)) * $BASE; 1095 } 1096 } 1097 else 1098 { 1099 push(@$x, 0); 1100 } 1101 1102 # @q will accumulate the final result, $q contains the current computed 1103 # part of the final result 1104 1105 @q = (); ($v2,$v1) = @$y[-2,-1]; 1106 $v2 = 0 unless $v2; 1107 while ($#$x > $#$y) 1108 { 1109 ($u2,$u1,$u0) = @$x[-3..-1]; 1110 $u2 = 0 unless $u2; 1111 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n" 1112 # if $v1 == 0; 1113 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$BASE+$u1)/$v1)); 1114 --$q while ($v2*$q > ($u0*$BASE+$u1-$q*$v1)*$BASE+$u2); 1115 if ($q) 1116 { 1117 ($car, $bar) = (0,0); 1118 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) 1119 { 1120 $prd = $q * $y->[$yi] + $car; 1121 $prd -= ($car = int($prd / $BASE)) * $BASE; 1122 $x->[$xi] += $BASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0)); 1123 } 1124 if ($x->[-1] < $car + $bar) 1125 { 1126 $car = 0; --$q; 1127 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi) 1128 { 1129 $x->[$xi] -= $BASE 1130 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE)); 1131 } 1132 } 1133 } 1134 pop(@$x); unshift(@q, $q); 1135 } 1136 if (wantarray) 1137 { 1138 @d = (); 1139 if ($dd != 1) 1140 { 1141 $car = 0; 1142 for $xi (reverse @$x) 1143 { 1144 $prd = $car * $BASE + $xi; 1145 $car = $prd - ($tmp = int($prd / $dd)) * $dd; 1146 unshift(@d, $tmp); 1147 } 1148 } 1149 else 1150 { 1151 @d = @$x; 1152 } 1153 @$x = @q; 1154 my $d = \@d; 1155 __strip_zeros($x); 1156 __strip_zeros($d); 1157 return ($x,$d); 1158 } 1159 @$x = @q; 1160 __strip_zeros($x); 1161 $x; 1162 } 1163 1164 ############################################################################## 1165 # testing 1166 1167 sub _acmp 1168 { 1169 # internal absolute post-normalized compare (ignore signs) 1170 # ref to array, ref to array, return <0, 0, >0 1171 # arrays must have at least one entry; this is not checked for 1172 my ($c,$cx,$cy) = @_; 1173 1174 # shortcut for short numbers 1175 return (($cx->[0] <=> $cy->[0]) <=> 0) 1176 if scalar @$cx == scalar @$cy && scalar @$cx == 1; 1177 1178 # fast comp based on number of array elements (aka pseudo-length) 1179 my $lxy = (scalar @$cx - scalar @$cy) 1180 # or length of first element if same number of elements (aka difference 0) 1181 || 1182 # need int() here because sometimes the last element is '00018' vs '18' 1183 (length(int($cx->[-1])) - length(int($cy->[-1]))); 1184 return -1 if $lxy < 0; # already differs, ret 1185 return 1 if $lxy > 0; # ditto 1186 1187 # manual way (abort if unequal, good for early ne) 1188 my $a; my $j = scalar @$cx; 1189 while (--$j >= 0) 1190 { 1191 last if ($a = $cx->[$j] - $cy->[$j]); 1192 } 1193 $a <=> 0; 1194 } 1195 1196 sub _len 1197 { 1198 # compute number of digits in base 10 1199 1200 # int() because add/sub sometimes leaves strings (like '00005') instead of 1201 # '5' in this place, thus causing length() to report wrong length 1202 my $cx = $_[1]; 1203 1204 (@$cx-1)*$BASE_LEN+length(int($cx->[-1])); 1205 } 1206 1207 sub _digit 1208 { 1209 # return the nth digit, negative values count backward 1210 # zero is rightmost, so _digit(123,0) will give 3 1211 my ($c,$x,$n) = @_; 1212 1213 my $len = _len('',$x); 1214 1215 $n = $len+$n if $n < 0; # -1 last, -2 second-to-last 1216 $n = abs($n); # if negative was too big 1217 $len--; $n = $len if $n > $len; # n to big? 1218 1219 my $elem = int($n / $BASE_LEN); # which array element 1220 my $digit = $n % $BASE_LEN; # which digit in this element 1221 $elem = '0' x $BASE_LEN . @$x[$elem]; # get element padded with 0's 1222 substr($elem,-$digit-1,1); 1223 } 1224 1225 sub _zeros 1226 { 1227 # return amount of trailing zeros in decimal 1228 # check each array elem in _m for having 0 at end as long as elem == 0 1229 # Upon finding a elem != 0, stop 1230 my $x = $_[1]; 1231 1232 return 0 if scalar @$x == 1 && $x->[0] == 0; 1233 1234 my $zeros = 0; my $elem; 1235 foreach my $e (@$x) 1236 { 1237 if ($e != 0) 1238 { 1239 $elem = "$e"; # preserve x 1240 $elem =~ s/.*?(0*$)/$1/; # strip anything not zero 1241 $zeros *= $BASE_LEN; # elems * 5 1242 $zeros += length($elem); # count trailing zeros 1243 last; # early out 1244 } 1245 $zeros ++; # real else branch: 50% slower! 1246 } 1247 $zeros; 1248 } 1249 1250 ############################################################################## 1251 # _is_* routines 1252 1253 sub _is_zero 1254 { 1255 # return true if arg is zero 1256 (((scalar @{$_[1]} == 1) && ($_[1]->[0] == 0))) <=> 0; 1257 } 1258 1259 sub _is_even 1260 { 1261 # return true if arg is even 1262 (!($_[1]->[0] & 1)) <=> 0; 1263 } 1264 1265 sub _is_odd 1266 { 1267 # return true if arg is even 1268 (($_[1]->[0] & 1)) <=> 0; 1269 } 1270 1271 sub _is_one 1272 { 1273 # return true if arg is one 1274 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 1) <=> 0; 1275 } 1276 1277 sub _is_two 1278 { 1279 # return true if arg is two 1280 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 2) <=> 0; 1281 } 1282 1283 sub _is_ten 1284 { 1285 # return true if arg is ten 1286 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 10) <=> 0; 1287 } 1288 1289 sub __strip_zeros 1290 { 1291 # internal normalization function that strips leading zeros from the array 1292 # args: ref to array 1293 my $s = shift; 1294 1295 my $cnt = scalar @$s; # get count of parts 1296 my $i = $cnt-1; 1297 push @$s,0 if $i < 0; # div might return empty results, so fix it 1298 1299 return $s if @$s == 1; # early out 1300 1301 #print "strip: cnt $cnt i $i\n"; 1302 # '0', '3', '4', '0', '0', 1303 # 0 1 2 3 4 1304 # cnt = 5, i = 4 1305 # i = 4 1306 # i = 3 1307 # => fcnt = cnt - i (5-2 => 3, cnt => 5-1 = 4, throw away from 4th pos) 1308 # >= 1: skip first part (this can be zero) 1309 while ($i > 0) { last if $s->[$i] != 0; $i--; } 1310 $i++; splice @$s,$i if ($i < $cnt); # $i cant be 0 1311 $s; 1312 } 1313 1314 ############################################################################### 1315 # check routine to test internal state for corruptions 1316 1317 sub _check 1318 { 1319 # used by the test suite 1320 my $x = $_[1]; 1321 1322 return "$x is not a reference" if !ref($x); 1323 1324 # are all parts are valid? 1325 my $i = 0; my $j = scalar @$x; my ($e,$try); 1326 while ($i < $j) 1327 { 1328 $e = $x->[$i]; $e = 'undef' unless defined $e; 1329 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e)"; 1330 last if $e !~ /^[+]?[0-9]+$/; 1331 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (stringify)"; 1332 last if "$e" !~ /^[+]?[0-9]+$/; 1333 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (cat-stringify)"; 1334 last if '' . "$e" !~ /^[+]?[0-9]+$/; 1335 $try = ' < 0 || >= $BASE; '."($x, $e)"; 1336 last if $e <0 || $e >= $BASE; 1337 # this test is disabled, since new/bnorm and certain ops (like early out 1338 # in add/sub) are allowed/expected to leave '00000' in some elements 1339 #$try = '=~ /^00+/; '."($x, $e)"; 1340 #last if $e =~ /^00+/; 1341 $i++; 1342 } 1343 return "Illegal part '$e' at pos $i (tested: $try)" if $i < $j; 1344 0; 1345 } 1346 1347 1348 ############################################################################### 1349 1350 sub _mod 1351 { 1352 # if possible, use mod shortcut 1353 my ($c,$x,$yo) = @_; 1354 1355 # slow way since $y to big 1356 if (scalar @$yo > 1) 1357 { 1358 my ($xo,$rem) = _div($c,$x,$yo); 1359 return $rem; 1360 } 1361 1362 my $y = $yo->[0]; 1363 # both are single element arrays 1364 if (scalar @$x == 1) 1365 { 1366 $x->[0] %= $y; 1367 return $x; 1368 } 1369 1370 # @y is a single element, but @x has more than one element 1371 my $b = $BASE % $y; 1372 if ($b == 0) 1373 { 1374 # when BASE % Y == 0 then (B * BASE) % Y == 0 1375 # (B * BASE) % $y + A % Y => A % Y 1376 # so need to consider only last element: O(1) 1377 $x->[0] %= $y; 1378 } 1379 elsif ($b == 1) 1380 { 1381 # else need to go through all elements: O(N), but loop is a bit simplified 1382 my $r = 0; 1383 foreach (@$x) 1384 { 1385 $r = ($r + $_) % $y; # not much faster, but heh... 1386 #$r += $_ % $y; $r %= $y; 1387 } 1388 $r = 0 if $r == $y; 1389 $x->[0] = $r; 1390 } 1391 else 1392 { 1393 # else need to go through all elements: O(N) 1394 my $r = 0; my $bm = 1; 1395 foreach (@$x) 1396 { 1397 $r = ($_ * $bm + $r) % $y; 1398 $bm = ($bm * $b) % $y; 1399 1400 #$r += ($_ % $y) * $bm; 1401 #$bm *= $b; 1402 #$bm %= $y; 1403 #$r %= $y; 1404 } 1405 $r = 0 if $r == $y; 1406 $x->[0] = $r; 1407 } 1408 splice (@$x,1); # keep one element of $x 1409 $x; 1410 } 1411 1412 ############################################################################## 1413 # shifts 1414 1415 sub _rsft 1416 { 1417 my ($c,$x,$y,$n) = @_; 1418 1419 if ($n != 10) 1420 { 1421 $n = _new($c,$n); return _div($c,$x, _pow($c,$n,$y)); 1422 } 1423 1424 # shortcut (faster) for shifting by 10) 1425 # multiples of $BASE_LEN 1426 my $dst = 0; # destination 1427 my $src = _num($c,$y); # as normal int 1428 my $xlen = (@$x-1)*$BASE_LEN+length(int($x->[-1])); # len of x in digits 1429 if ($src >= $xlen or ($src == $xlen and ! defined $x->[1])) 1430 { 1431 # 12345 67890 shifted right by more than 10 digits => 0 1432 splice (@$x,1); # leave only one element 1433 $x->[0] = 0; # set to zero 1434 return $x; 1435 } 1436 my $rem = $src % $BASE_LEN; # remainder to shift 1437 $src = int($src / $BASE_LEN); # source 1438 if ($rem == 0) 1439 { 1440 splice (@$x,0,$src); # even faster, 38.4 => 39.3 1441 } 1442 else 1443 { 1444 my $len = scalar @$x - $src; # elems to go 1445 my $vd; my $z = '0'x $BASE_LEN; 1446 $x->[scalar @$x] = 0; # avoid || 0 test inside loop 1447 while ($dst < $len) 1448 { 1449 $vd = $z.$x->[$src]; 1450 $vd = substr($vd,-$BASE_LEN,$BASE_LEN-$rem); 1451 $src++; 1452 $vd = substr($z.$x->[$src],-$rem,$rem) . $vd; 1453 $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN; 1454 $x->[$dst] = int($vd); 1455 $dst++; 1456 } 1457 splice (@$x,$dst) if $dst > 0; # kill left-over array elems 1458 pop @$x if $x->[-1] == 0 && @$x > 1; # kill last element if 0 1459 } # else rem == 0 1460 $x; 1461 } 1462 1463 sub _lsft 1464 { 1465 my ($c,$x,$y,$n) = @_; 1466 1467 if ($n != 10) 1468 { 1469 $n = _new($c,$n); return _mul($c,$x, _pow($c,$n,$y)); 1470 } 1471 1472 # shortcut (faster) for shifting by 10) since we are in base 10eX 1473 # multiples of $BASE_LEN: 1474 my $src = scalar @$x; # source 1475 my $len = _num($c,$y); # shift-len as normal int 1476 my $rem = $len % $BASE_LEN; # remainder to shift 1477 my $dst = $src + int($len/$BASE_LEN); # destination 1478 my $vd; # further speedup 1479 $x->[$src] = 0; # avoid first ||0 for speed 1480 my $z = '0' x $BASE_LEN; 1481 while ($src >= 0) 1482 { 1483 $vd = $x->[$src]; $vd = $z.$vd; 1484 $vd = substr($vd,-$BASE_LEN+$rem,$BASE_LEN-$rem); 1485 $vd .= $src > 0 ? substr($z.$x->[$src-1],-$BASE_LEN,$rem) : '0' x $rem; 1486 $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN; 1487 $x->[$dst] = int($vd); 1488 $dst--; $src--; 1489 } 1490 # set lowest parts to 0 1491 while ($dst >= 0) { $x->[$dst--] = 0; } 1492 # fix spurios last zero element 1493 splice @$x,-1 if $x->[-1] == 0; 1494 $x; 1495 } 1496 1497 sub _pow 1498 { 1499 # power of $x to $y 1500 # ref to array, ref to array, return ref to array 1501 my ($c,$cx,$cy) = @_; 1502 1503 if (scalar @$cy == 1 && $cy->[0] == 0) 1504 { 1505 splice (@$cx,1); $cx->[0] = 1; # y == 0 => x => 1 1506 return $cx; 1507 } 1508 if ((scalar @$cx == 1 && $cx->[0] == 1) || # x == 1 1509 (scalar @$cy == 1 && $cy->[0] == 1)) # or y == 1 1510 { 1511 return $cx; 1512 } 1513 if (scalar @$cx == 1 && $cx->[0] == 0) 1514 { 1515 splice (@$cx,1); $cx->[0] = 0; # 0 ** y => 0 (if not y <= 0) 1516 return $cx; 1517 } 1518 1519 my $pow2 = _one(); 1520 1521 my $y_bin = _as_bin($c,$cy); $y_bin =~ s/^0b//; 1522 my $len = length($y_bin); 1523 while (--$len > 0) 1524 { 1525 _mul($c,$pow2,$cx) if substr($y_bin,$len,1) eq '1'; # is odd? 1526 _mul($c,$cx,$cx); 1527 } 1528 1529 _mul($c,$cx,$pow2); 1530 $cx; 1531 } 1532 1533 sub _nok 1534 { 1535 # n over k 1536 # ref to array, return ref to array 1537 my ($c,$n,$k) = @_; 1538 1539 # ( 7 ) 7! 7*6*5 * 4*3*2*1 7 * 6 * 5 1540 # ( - ) = --------- = --------------- = --------- 1541 # ( 3 ) 3! (7-3)! 3*2*1 * 4*3*2*1 3 * 2 * 1 1542 1543 # compute n - k + 2 (so we start with 5 in the example above) 1544 my $x = _copy($c,$n); 1545 1546 _sub($c,$n,$k); 1547 if (!_is_one($c,$n)) 1548 { 1549 _inc($c,$n); 1550 my $f = _copy($c,$n); _inc($c,$f); # n = 5, f = 6, d = 2 1551 my $d = _two($c); 1552 while (_acmp($c,$f,$x) <= 0) # f < n ? 1553 { 1554 # n = (n * f / d) == 5 * 6 / 2 => n == 3 1555 $n = _mul($c,$n,$f); $n = _div($c,$n,$d); 1556 # f = 7, d = 3 1557 _inc($c,$f); _inc($c,$d); 1558 } 1559 } 1560 else 1561 { 1562 # keep ref to $n and set it to 1 1563 splice (@$n,1); $n->[0] = 1; 1564 } 1565 $n; 1566 } 1567 1568 my @factorials = ( 1569 1, 1570 1, 1571 2, 1572 2*3, 1573 2*3*4, 1574 2*3*4*5, 1575 2*3*4*5*6, 1576 2*3*4*5*6*7, 1577 ); 1578 1579 sub _fac 1580 { 1581 # factorial of $x 1582 # ref to array, return ref to array 1583 my ($c,$cx) = @_; 1584 1585 if ((@$cx == 1) && ($cx->[0] <= 7)) 1586 { 1587 $cx->[0] = $factorials[$cx->[0]]; # 0 => 1, 1 => 1, 2 => 2 etc. 1588 return $cx; 1589 } 1590 1591 if ((@$cx == 1) && # we do this only if $x >= 12 and $x <= 7000 1592 ($cx->[0] >= 12 && $cx->[0] < 7000)) 1593 { 1594 1595 # Calculate (k-j) * (k-j+1) ... k .. (k+j-1) * (k + j) 1596 # See http://blogten.blogspot.com/2007/01/calculating-n.html 1597 # The above series can be expressed as factors: 1598 # k * k - (j - i) * 2 1599 # We cache k*k, and calculate (j * j) as the sum of the first j odd integers 1600 1601 # This will not work when N exceeds the storage of a Perl scalar, however, 1602 # in this case the algorithm would be way to slow to terminate, anyway. 1603 1604 # As soon as the last element of $cx is 0, we split it up and remember 1605 # how many zeors we got so far. The reason is that n! will accumulate 1606 # zeros at the end rather fast. 1607 my $zero_elements = 0; 1608 1609 # If n is even, set n = n -1 1610 my $k = _num($c,$cx); my $even = 1; 1611 if (($k & 1) == 0) 1612 { 1613 $even = $k; $k --; 1614 } 1615 # set k to the center point 1616 $k = ($k + 1) / 2; 1617 # print "k $k even: $even\n"; 1618 # now calculate k * k 1619 my $k2 = $k * $k; 1620 my $odd = 1; my $sum = 1; 1621 my $i = $k - 1; 1622 # keep reference to x 1623 my $new_x = _new($c, $k * $even); 1624 @$cx = @$new_x; 1625 if ($cx->[0] == 0) 1626 { 1627 $zero_elements ++; shift @$cx; 1628 } 1629 # print STDERR "x = ", _str($c,$cx),"\n"; 1630 my $BASE2 = int(sqrt($BASE))-1; 1631 my $j = 1; 1632 while ($j <= $i) 1633 { 1634 my $m = ($k2 - $sum); $odd += 2; $sum += $odd; $j++; 1635 while ($j <= $i && ($m < $BASE2) && (($k2 - $sum) < $BASE2)) 1636 { 1637 $m *= ($k2 - $sum); 1638 $odd += 2; $sum += $odd; $j++; 1639 # print STDERR "\n k2 $k2 m $m sum $sum odd $odd\n"; sleep(1); 1640 } 1641 if ($m < $BASE) 1642 { 1643 _mul($c,$cx,[$m]); 1644 } 1645 else 1646 { 1647 _mul($c,$cx,$c->_new($m)); 1648 } 1649 if ($cx->[0] == 0) 1650 { 1651 $zero_elements ++; shift @$cx; 1652 } 1653 # print STDERR "Calculate $k2 - $sum = $m (x = ", _str($c,$cx),")\n"; 1654 } 1655 # multiply in the zeros again 1656 unshift @$cx, (0) x $zero_elements; 1657 return $cx; 1658 } 1659 1660 # go forward until $base is exceeded 1661 # limit is either $x steps (steps == 100 means a result always too high) or 1662 # $base. 1663 my $steps = 100; $steps = $cx->[0] if @$cx == 1; 1664 my $r = 2; my $cf = 3; my $step = 2; my $last = $r; 1665 while ($r*$cf < $BASE && $step < $steps) 1666 { 1667 $last = $r; $r *= $cf++; $step++; 1668 } 1669 if ((@$cx == 1) && $step == $cx->[0]) 1670 { 1671 # completely done, so keep reference to $x and return 1672 $cx->[0] = $r; 1673 return $cx; 1674 } 1675 1676 # now we must do the left over steps 1677 my $n; # steps still to do 1678 if (scalar @$cx == 1) 1679 { 1680 $n = $cx->[0]; 1681 } 1682 else 1683 { 1684 $n = _copy($c,$cx); 1685 } 1686 1687 # Set $cx to the last result below $BASE (but keep ref to $x) 1688 $cx->[0] = $last; splice (@$cx,1); 1689 # As soon as the last element of $cx is 0, we split it up and remember 1690 # how many zeors we got so far. The reason is that n! will accumulate 1691 # zeros at the end rather fast. 1692 my $zero_elements = 0; 1693 1694 # do left-over steps fit into a scalar? 1695 if (ref $n eq 'ARRAY') 1696 { 1697 # No, so use slower inc() & cmp() 1698 # ($n is at least $BASE here) 1699 my $base_2 = int(sqrt($BASE)) - 1; 1700 #print STDERR "base_2: $base_2\n"; 1701 while ($step < $base_2) 1702 { 1703 if ($cx->[0] == 0) 1704 { 1705 $zero_elements ++; shift @$cx; 1706 } 1707 my $b = $step * ($step + 1); $step += 2; 1708 _mul($c,$cx,[$b]); 1709 } 1710 $step = [$step]; 1711 while (_acmp($c,$step,$n) <= 0) 1712 { 1713 if ($cx->[0] == 0) 1714 { 1715 $zero_elements ++; shift @$cx; 1716 } 1717 _mul($c,$cx,$step); _inc($c,$step); 1718 } 1719 } 1720 else 1721 { 1722 # Yes, so we can speed it up slightly 1723 1724 # print "# left over steps $n\n"; 1725 1726 my $base_4 = int(sqrt(sqrt($BASE))) - 2; 1727 #print STDERR "base_4: $base_4\n"; 1728 my $n4 = $n - 4; 1729 while ($step < $n4 && $step < $base_4) 1730 { 1731 if ($cx->[0] == 0) 1732 { 1733 $zero_elements ++; shift @$cx; 1734 } 1735 my $b = $step * ($step + 1); $step += 2; $b *= $step * ($step + 1); $step += 2; 1736 _mul($c,$cx,[$b]); 1737 } 1738 my $base_2 = int(sqrt($BASE)) - 1; 1739 my $n2 = $n - 2; 1740 #print STDERR "base_2: $base_2\n"; 1741 while ($step < $n2 && $step < $base_2) 1742 { 1743 if ($cx->[0] == 0) 1744 { 1745 $zero_elements ++; shift @$cx; 1746 } 1747 my $b = $step * ($step + 1); $step += 2; 1748 _mul($c,$cx,[$b]); 1749 } 1750 # do what's left over 1751 while ($step <= $n) 1752 { 1753 _mul($c,$cx,[$step]); $step++; 1754 if ($cx->[0] == 0) 1755 { 1756 $zero_elements ++; shift @$cx; 1757 } 1758 } 1759 } 1760 # multiply in the zeros again 1761 unshift @$cx, (0) x $zero_elements; 1762 $cx; # return result 1763 } 1764 1765 ############################################################################# 1766 1767 sub _log_int 1768 { 1769 # calculate integer log of $x to base $base 1770 # ref to array, ref to array - return ref to array 1771 my ($c,$x,$base) = @_; 1772 1773 # X == 0 => NaN 1774 return if (scalar @$x == 1 && $x->[0] == 0); 1775 # BASE 0 or 1 => NaN 1776 return if (scalar @$base == 1 && $base->[0] < 2); 1777 my $cmp = _acmp($c,$x,$base); # X == BASE => 1 1778 if ($cmp == 0) 1779 { 1780 splice (@$x,1); $x->[0] = 1; 1781 return ($x,1) 1782 } 1783 # X < BASE 1784 if ($cmp < 0) 1785 { 1786 splice (@$x,1); $x->[0] = 0; 1787 return ($x,undef); 1788 } 1789 1790 my $x_org = _copy($c,$x); # preserve x 1791 splice(@$x,1); $x->[0] = 1; # keep ref to $x 1792 1793 # Compute a guess for the result based on: 1794 # $guess = int ( length_in_base_10(X) / ( log(base) / log(10) ) ) 1795 my $len = _len($c,$x_org); 1796 my $log = log($base->[-1]) / log(10); 1797 1798 # for each additional element in $base, we add $BASE_LEN to the result, 1799 # based on the observation that log($BASE,10) is BASE_LEN and 1800 # log(x*y) == log(x) + log(y): 1801 $log += ((scalar @$base)-1) * $BASE_LEN; 1802 1803 # calculate now a guess based on the values obtained above: 1804 my $res = int($len / $log); 1805 1806 $x->[0] = $res; 1807 my $trial = _pow ($c, _copy($c, $base), $x); 1808 my $a = _acmp($c,$trial,$x_org); 1809 1810 # print STDERR "# trial ", _str($c,$x)," was: $a (0 = exact, -1 too small, +1 too big)\n"; 1811 1812 # found an exact result? 1813 return ($x,1) if $a == 0; 1814 1815 if ($a > 0) 1816 { 1817 # or too big 1818 _div($c,$trial,$base); _dec($c, $x); 1819 while (($a = _acmp($c,$trial,$x_org)) > 0) 1820 { 1821 # print STDERR "# big _log_int at ", _str($c,$x), "\n"; 1822 _div($c,$trial,$base); _dec($c, $x); 1823 } 1824 # result is now exact (a == 0), or too small (a < 0) 1825 return ($x, $a == 0 ? 1 : 0); 1826 } 1827 1828 # else: result was to small 1829 _mul($c,$trial,$base); 1830 1831 # did we now get the right result? 1832 $a = _acmp($c,$trial,$x_org); 1833 1834 if ($a == 0) # yes, exactly 1835 { 1836 _inc($c, $x); 1837 return ($x,1); 1838 } 1839 return ($x,0) if $a > 0; 1840 1841 # Result still too small (we should come here only if the estimate above 1842 # was very off base): 1843 1844 # Now let the normal trial run obtain the real result 1845 # Simple loop that increments $x by 2 in each step, possible overstepping 1846 # the real result 1847 1848 my $base_mul = _mul($c, _copy($c,$base), $base); # $base * $base 1849 1850 while (($a = _acmp($c,$trial,$x_org)) < 0) 1851 { 1852 # print STDERR "# small _log_int at ", _str($c,$x), "\n"; 1853 _mul($c,$trial,$base_mul); _add($c, $x, [2]); 1854 } 1855 1856 my $exact = 1; 1857 if ($a > 0) 1858 { 1859 # overstepped the result 1860 _dec($c, $x); 1861 _div($c,$trial,$base); 1862 $a = _acmp($c,$trial,$x_org); 1863 if ($a > 0) 1864 { 1865 _dec($c, $x); 1866 } 1867 $exact = 0 if $a != 0; # a = -1 => not exact result, a = 0 => exact 1868 } 1869 1870 ($x,$exact); # return result 1871 } 1872 1873 # for debugging: 1874 use constant DEBUG => 0; 1875 my $steps = 0; 1876 sub steps { $steps }; 1877 1878 sub _sqrt 1879 { 1880 # square-root of $x in place 1881 # Compute a guess of the result (by rule of thumb), then improve it via 1882 # Newton's method. 1883 my ($c,$x) = @_; 1884 1885 if (scalar @$x == 1) 1886 { 1887 # fits into one Perl scalar, so result can be computed directly 1888 $x->[0] = int(sqrt($x->[0])); 1889 return $x; 1890 } 1891 my $y = _copy($c,$x); 1892 # hopefully _len/2 is < $BASE, the -1 is to always undershot the guess 1893 # since our guess will "grow" 1894 my $l = int((_len($c,$x)-1) / 2); 1895 1896 my $lastelem = $x->[-1]; # for guess 1897 my $elems = scalar @$x - 1; 1898 # not enough digits, but could have more? 1899 if ((length($lastelem) <= 3) && ($elems > 1)) 1900 { 1901 # right-align with zero pad 1902 my $len = length($lastelem) & 1; 1903 print "$lastelem => " if DEBUG; 1904 $lastelem .= substr($x->[-2] . '0' x $BASE_LEN,0,$BASE_LEN); 1905 # former odd => make odd again, or former even to even again 1906 $lastelem = $lastelem / 10 if (length($lastelem) & 1) != $len; 1907 print "$lastelem\n" if DEBUG; 1908 } 1909 1910 # construct $x (instead of _lsft($c,$x,$l,10) 1911 my $r = $l % $BASE_LEN; # 10000 00000 00000 00000 ($BASE_LEN=5) 1912 $l = int($l / $BASE_LEN); 1913 print "l = $l " if DEBUG; 1914 1915 splice @$x,$l; # keep ref($x), but modify it 1916 1917 # we make the first part of the guess not '1000...0' but int(sqrt($lastelem)) 1918 # that gives us: 1919 # 14400 00000 => sqrt(14400) => guess first digits to be 120 1920 # 144000 000000 => sqrt(144000) => guess 379 1921 1922 print "$lastelem (elems $elems) => " if DEBUG; 1923 $lastelem = $lastelem / 10 if ($elems & 1 == 1); # odd or even? 1924 my $g = sqrt($lastelem); $g =~ s/\.//; # 2.345 => 2345 1925 $r -= 1 if $elems & 1 == 0; # 70 => 7 1926 1927 # padd with zeros if result is too short 1928 $x->[$l--] = int(substr($g . '0' x $r,0,$r+1)); 1929 print "now ",$x->[-1] if DEBUG; 1930 print " would have been ", int('1' . '0' x $r),"\n" if DEBUG; 1931 1932 # If @$x > 1, we could compute the second elem of the guess, too, to create 1933 # an even better guess. Not implemented yet. Does it improve performance? 1934 $x->[$l--] = 0 while ($l >= 0); # all other digits of guess are zero 1935 1936 print "start x= ",_str($c,$x),"\n" if DEBUG; 1937 my $two = _two(); 1938 my $last = _zero(); 1939 my $lastlast = _zero(); 1940 $steps = 0 if DEBUG; 1941 while (_acmp($c,$last,$x) != 0 && _acmp($c,$lastlast,$x) != 0) 1942 { 1943 $steps++ if DEBUG; 1944 $lastlast = _copy($c,$last); 1945 $last = _copy($c,$x); 1946 _add($c,$x, _div($c,_copy($c,$y),$x)); 1947 _div($c,$x, $two ); 1948 print " x= ",_str($c,$x),"\n" if DEBUG; 1949 } 1950 print "\nsteps in sqrt: $steps, " if DEBUG; 1951 _dec($c,$x) if _acmp($c,$y,_mul($c,_copy($c,$x),$x)) < 0; # overshot? 1952 print " final ",$x->[-1],"\n" if DEBUG; 1953 $x; 1954 } 1955 1956 sub _root 1957 { 1958 # take n'th root of $x in place (n >= 3) 1959 my ($c,$x,$n) = @_; 1960 1961 if (scalar @$x == 1) 1962 { 1963 if (scalar @$n > 1) 1964 { 1965 # result will always be smaller than 2 so trunc to 1 at once 1966 $x->[0] = 1; 1967 } 1968 else 1969 { 1970 # fits into one Perl scalar, so result can be computed directly 1971 # cannot use int() here, because it rounds wrongly (try 1972 # (81 ** 3) ** (1/3) to see what I mean) 1973 #$x->[0] = int( $x->[0] ** (1 / $n->[0]) ); 1974 # round to 8 digits, then truncate result to integer 1975 $x->[0] = int ( sprintf ("%.8f", $x->[0] ** (1 / $n->[0]) ) ); 1976 } 1977 return $x; 1978 } 1979 1980 # we know now that X is more than one element long 1981 1982 # if $n is a power of two, we can repeatedly take sqrt($X) and find the 1983 # proper result, because sqrt(sqrt($x)) == root($x,4) 1984 my $b = _as_bin($c,$n); 1985 if ($b =~ /0b1(0+)$/) 1986 { 1987 my $count = CORE::length($1); # 0b100 => len('00') => 2 1988 my $cnt = $count; # counter for loop 1989 unshift (@$x, 0); # add one element, together with one 1990 # more below in the loop this makes 2 1991 while ($cnt-- > 0) 1992 { 1993 # 'inflate' $X by adding one element, basically computing 1994 # $x * $BASE * $BASE. This gives us more $BASE_LEN digits for result 1995 # since len(sqrt($X)) approx == len($x) / 2. 1996 unshift (@$x, 0); 1997 # calculate sqrt($x), $x is now one element to big, again. In the next 1998 # round we make that two, again. 1999 _sqrt($c,$x); 2000 } 2001 # $x is now one element to big, so truncate result by removing it 2002 splice (@$x,0,1); 2003 } 2004 else 2005 { 2006 # trial computation by starting with 2,4,8,16 etc until we overstep 2007 my $step; 2008 my $trial = _two(); 2009 2010 # while still to do more than X steps 2011 do 2012 { 2013 $step = _two(); 2014 while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0) 2015 { 2016 _mul ($c, $step, [2]); 2017 _add ($c, $trial, $step); 2018 } 2019 2020 # hit exactly? 2021 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) == 0) 2022 { 2023 @$x = @$trial; # make copy while preserving ref to $x 2024 return $x; 2025 } 2026 # overstepped, so go back on step 2027 _sub($c, $trial, $step); 2028 } while (scalar @$step > 1 || $step->[0] > 128); 2029 2030 # reset step to 2 2031 $step = _two(); 2032 # add two, because $trial cannot be exactly the result (otherwise we would 2033 # alrady have found it) 2034 _add($c, $trial, $step); 2035 2036 # and now add more and more (2,4,6,8,10 etc) 2037 while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0) 2038 { 2039 _add ($c, $trial, $step); 2040 } 2041 2042 # hit not exactly? (overstepped) 2043 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0) 2044 { 2045 _dec($c,$trial); 2046 } 2047 2048 # hit not exactly? (overstepped) 2049 # 80 too small, 81 slightly too big, 82 too big 2050 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0) 2051 { 2052 _dec ($c, $trial); 2053 } 2054 2055 @$x = @$trial; # make copy while preserving ref to $x 2056 return $x; 2057 } 2058 $x; 2059 } 2060 2061 ############################################################################## 2062 # binary stuff 2063 2064 sub _and 2065 { 2066 my ($c,$x,$y) = @_; 2067 2068 # the shortcut makes equal, large numbers _really_ fast, and makes only a 2069 # very small performance drop for small numbers (e.g. something with less 2070 # than 32 bit) Since we optimize for large numbers, this is enabled. 2071 return $x if _acmp($c,$x,$y) == 0; # shortcut 2072 2073 my $m = _one(); my ($xr,$yr); 2074 my $mask = $AND_MASK; 2075 2076 my $x1 = $x; 2077 my $y1 = _copy($c,$y); # make copy 2078 $x = _zero(); 2079 my ($b,$xrr,$yrr); 2080 use integer; 2081 while (!_is_zero($c,$x1) && !_is_zero($c,$y1)) 2082 { 2083 ($x1, $xr) = _div($c,$x1,$mask); 2084 ($y1, $yr) = _div($c,$y1,$mask); 2085 2086 # make ints() from $xr, $yr 2087 # this is when the AND_BITS are greater than $BASE and is slower for 2088 # small (<256 bits) numbers, but faster for large numbers. Disabled 2089 # due to KISS principle 2090 2091 # $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; } 2092 # $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; } 2093 # _add($c,$x, _mul($c, _new( $c, ($xrr & $yrr) ), $m) ); 2094 2095 # 0+ due to '&' doesn't work in strings 2096 _add($c,$x, _mul($c, [ 0+$xr->[0] & 0+$yr->[0] ], $m) ); 2097 _mul($c,$m,$mask); 2098 } 2099 $x; 2100 } 2101 2102 sub _xor 2103 { 2104 my ($c,$x,$y) = @_; 2105 2106 return _zero() if _acmp($c,$x,$y) == 0; # shortcut (see -and) 2107 2108 my $m = _one(); my ($xr,$yr); 2109 my $mask = $XOR_MASK; 2110 2111 my $x1 = $x; 2112 my $y1 = _copy($c,$y); # make copy 2113 $x = _zero(); 2114 my ($b,$xrr,$yrr); 2115 use integer; 2116 while (!_is_zero($c,$x1) && !_is_zero($c,$y1)) 2117 { 2118 ($x1, $xr) = _div($c,$x1,$mask); 2119 ($y1, $yr) = _div($c,$y1,$mask); 2120 # make ints() from $xr, $yr (see _and()) 2121 #$b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; } 2122 #$b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; } 2123 #_add($c,$x, _mul($c, _new( $c, ($xrr ^ $yrr) ), $m) ); 2124 2125 # 0+ due to '^' doesn't work in strings 2126 _add($c,$x, _mul($c, [ 0+$xr->[0] ^ 0+$yr->[0] ], $m) ); 2127 _mul($c,$m,$mask); 2128 } 2129 # the loop stops when the shorter of the two numbers is exhausted 2130 # the remainder of the longer one will survive bit-by-bit, so we simple 2131 # multiply-add it in 2132 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1); 2133 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1); 2134 2135 $x; 2136 } 2137 2138 sub _or 2139 { 2140 my ($c,$x,$y) = @_; 2141 2142 return $x if _acmp($c,$x,$y) == 0; # shortcut (see _and) 2143 2144 my $m = _one(); my ($xr,$yr); 2145 my $mask = $OR_MASK; 2146 2147 my $x1 = $x; 2148 my $y1 = _copy($c,$y); # make copy 2149 $x = _zero(); 2150 my ($b,$xrr,$yrr); 2151 use integer; 2152 while (!_is_zero($c,$x1) && !_is_zero($c,$y1)) 2153 { 2154 ($x1, $xr) = _div($c,$x1,$mask); 2155 ($y1, $yr) = _div($c,$y1,$mask); 2156 # make ints() from $xr, $yr (see _and()) 2157 # $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; } 2158 # $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; } 2159 # _add($c,$x, _mul($c, _new( $c, ($xrr | $yrr) ), $m) ); 2160 2161 # 0+ due to '|' doesn't work in strings 2162 _add($c,$x, _mul($c, [ 0+$xr->[0] | 0+$yr->[0] ], $m) ); 2163 _mul($c,$m,$mask); 2164 } 2165 # the loop stops when the shorter of the two numbers is exhausted 2166 # the remainder of the longer one will survive bit-by-bit, so we simple 2167 # multiply-add it in 2168 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1); 2169 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1); 2170 2171 $x; 2172 } 2173 2174 sub _as_hex 2175 { 2176 # convert a decimal number to hex (ref to array, return ref to string) 2177 my ($c,$x) = @_; 2178 2179 # fits into one element (handle also 0x0 case) 2180 return sprintf("0x%x",$x->[0]) if @$x == 1; 2181 2182 my $x1 = _copy($c,$x); 2183 2184 my $es = ''; 2185 my ($xr, $h, $x10000); 2186 if ($] >= 5.006) 2187 { 2188 $x10000 = [ 0x10000 ]; $h = 'h4'; 2189 } 2190 else 2191 { 2192 $x10000 = [ 0x1000 ]; $h = 'h3'; 2193 } 2194 while (@$x1 != 1 || $x1->[0] != 0) # _is_zero() 2195 { 2196 ($x1, $xr) = _div($c,$x1,$x10000); 2197 $es .= unpack($h,pack('V',$xr->[0])); 2198 } 2199 $es = reverse $es; 2200 $es =~ s/^[0]+//; # strip leading zeros 2201 '0x' . $es; # return result prepended with 0x 2202 } 2203 2204 sub _as_bin 2205 { 2206 # convert a decimal number to bin (ref to array, return ref to string) 2207 my ($c,$x) = @_; 2208 2209 # fits into one element (and Perl recent enough), handle also 0b0 case 2210 # handle zero case for older Perls 2211 if ($] <= 5.005 && @$x == 1 && $x->[0] == 0) 2212 { 2213 my $t = '0b0'; return $t; 2214 } 2215 if (@$x == 1 && $] >= 5.006) 2216 { 2217 my $t = sprintf("0b%b",$x->[0]); 2218 return $t; 2219 } 2220 my $x1 = _copy($c,$x); 2221 2222 my $es = ''; 2223 my ($xr, $b, $x10000); 2224 if ($] >= 5.006) 2225 { 2226 $x10000 = [ 0x10000 ]; $b = 'b16'; 2227 } 2228 else 2229 { 2230 $x10000 = [ 0x1000 ]; $b = 'b12'; 2231 } 2232 while (!(@$x1 == 1 && $x1->[0] == 0)) # _is_zero() 2233 { 2234 ($x1, $xr) = _div($c,$x1,$x10000); 2235 $es .= unpack($b,pack('v',$xr->[0])); 2236 } 2237 $es = reverse $es; 2238 $es =~ s/^[0]+//; # strip leading zeros 2239 '0b' . $es; # return result prepended with 0b 2240 } 2241 2242 sub _as_oct 2243 { 2244 # convert a decimal number to octal (ref to array, return ref to string) 2245 my ($c,$x) = @_; 2246 2247 # fits into one element (handle also 0 case) 2248 return sprintf("0%o",$x->[0]) if @$x == 1; 2249 2250 my $x1 = _copy($c,$x); 2251 2252 my $es = ''; 2253 my $xr; 2254 my $x1000 = [ 0100000 ]; 2255 while (@$x1 != 1 || $x1->[0] != 0) # _is_zero() 2256 { 2257 ($x1, $xr) = _div($c,$x1,$x1000); 2258 $es .= reverse sprintf("%05o", $xr->[0]); 2259 } 2260 $es = reverse $es; 2261 $es =~ s/^[0]+//; # strip leading zeros 2262 '0' . $es; # return result prepended with 0 2263 } 2264 2265 sub _from_oct 2266 { 2267 # convert a octal number to decimal (string, return ref to array) 2268 my ($c,$os) = @_; 2269 2270 # for older Perls, play safe 2271 my $m = [ 0100000 ]; 2272 my $d = 5; # 5 digits at a time 2273 2274 my $mul = _one(); 2275 my $x = _zero(); 2276 2277 my $len = int( (length($os)-1)/$d ); # $d digit parts, w/o the '0' 2278 my $val; my $i = -$d; 2279 while ($len >= 0) 2280 { 2281 $val = substr($os,$i,$d); # get oct digits 2282 $val = CORE::oct($val); 2283 $i -= $d; $len --; 2284 my $adder = [ $val ]; 2285 _add ($c, $x, _mul ($c, $adder, $mul ) ) if $val != 0; 2286 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul 2287 } 2288 $x; 2289 } 2290 2291 sub _from_hex 2292 { 2293 # convert a hex number to decimal (string, return ref to array) 2294 my ($c,$hs) = @_; 2295 2296 my $m = _new($c, 0x10000000); # 28 bit at a time (<32 bit!) 2297 my $d = 7; # 7 digits at a time 2298 if ($] <= 5.006) 2299 { 2300 # for older Perls, play safe 2301 $m = [ 0x10000 ]; # 16 bit at a time (<32 bit!) 2302 $d = 4; # 4 digits at a time 2303 } 2304 2305 my $mul = _one(); 2306 my $x = _zero(); 2307 2308 my $len = int( (length($hs)-2)/$d ); # $d digit parts, w/o the '0x' 2309 my $val; my $i = -$d; 2310 while ($len >= 0) 2311 { 2312 $val = substr($hs,$i,$d); # get hex digits 2313 $val =~ s/^0x// if $len == 0; # for last part only because 2314 $val = CORE::hex($val); # hex does not like wrong chars 2315 $i -= $d; $len --; 2316 my $adder = [ $val ]; 2317 # if the resulting number was to big to fit into one element, create a 2318 # two-element version (bug found by Mark Lakata - Thanx!) 2319 if (CORE::length($val) > $BASE_LEN) 2320 { 2321 $adder = _new($c,$val); 2322 } 2323 _add ($c, $x, _mul ($c, $adder, $mul ) ) if $val != 0; 2324 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul 2325 } 2326 $x; 2327 } 2328 2329 sub _from_bin 2330 { 2331 # convert a hex number to decimal (string, return ref to array) 2332 my ($c,$bs) = @_; 2333 2334 # instead of converting X (8) bit at a time, it is faster to "convert" the 2335 # number to hex, and then call _from_hex. 2336 2337 my $hs = $bs; 2338 $hs =~ s/^[+-]?0b//; # remove sign and 0b 2339 my $l = length($hs); # bits 2340 $hs = '0' x (8-($l % 8)) . $hs if ($l % 8) != 0; # padd left side w/ 0 2341 my $h = '0x' . unpack('H*', pack ('B*', $hs)); # repack as hex 2342 2343 $c->_from_hex($h); 2344 } 2345 2346 ############################################################################## 2347 # special modulus functions 2348 2349 sub _modinv 2350 { 2351 # modular inverse 2352 my ($c,$x,$y) = @_; 2353 2354 my $u = _zero($c); my $u1 = _one($c); 2355 my $a = _copy($c,$y); my $b = _copy($c,$x); 2356 2357 # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the 2358 # result ($u) at the same time. See comments in BigInt for why this works. 2359 my $q; 2360 ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1 2361 my $sign = 1; 2362 while (!_is_zero($c,$b)) 2363 { 2364 my $t = _add($c, # step 2: 2365 _mul($c,_copy($c,$u1), $q) , # t = u1 * q 2366 $u ); # + u 2367 $u = $u1; # u = u1, u1 = t 2368 $u1 = $t; 2369 $sign = -$sign; 2370 ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1 2371 } 2372 2373 # if the gcd is not 1, then return NaN 2374 return (undef,undef) unless _is_one($c,$a); 2375 2376 ($u1, $sign == 1 ? '+' : '-'); 2377 } 2378 2379 sub _modpow 2380 { 2381 # modulus of power ($x ** $y) % $z 2382 my ($c,$num,$exp,$mod) = @_; 2383 2384 # in the trivial case, 2385 if (_is_one($c,$mod)) 2386 { 2387 splice @$num,0,1; $num->[0] = 0; 2388 return $num; 2389 } 2390 if ((scalar @$num == 1) && (($num->[0] == 0) || ($num->[0] == 1))) 2391 { 2392 $num->[0] = 1; 2393 return $num; 2394 } 2395 2396 # $num = _mod($c,$num,$mod); # this does not make it faster 2397 2398 my $acc = _copy($c,$num); my $t = _one(); 2399 2400 my $expbin = _as_bin($c,$exp); $expbin =~ s/^0b//; 2401 my $len = length($expbin); 2402 while (--$len >= 0) 2403 { 2404 if ( substr($expbin,$len,1) eq '1') # is_odd 2405 { 2406 _mul($c,$t,$acc); 2407 $t = _mod($c,$t,$mod); 2408 } 2409 _mul($c,$acc,$acc); 2410 $acc = _mod($c,$acc,$mod); 2411 } 2412 @$num = @$t; 2413 $num; 2414 } 2415 2416 sub _gcd 2417 { 2418 # greatest common divisor 2419 my ($c,$x,$y) = @_; 2420 2421 while ( (scalar @$y != 1) || ($y->[0] != 0) ) # while ($y != 0) 2422 { 2423 my $t = _copy($c,$y); 2424 $y = _mod($c, $x, $y); 2425 $x = $t; 2426 } 2427 $x; 2428 } 2429 2430 ############################################################################## 2431 ############################################################################## 2432 2433 1; 2434 __END__ 2435 2436 =head1 NAME 2437 2438 Math::BigInt::Calc - Pure Perl module to support Math::BigInt 2439 2440 =head1 SYNOPSIS 2441 2442 Provides support for big integer calculations. Not intended to be used by other 2443 modules. Other modules which sport the same functions can also be used to support 2444 Math::BigInt, like Math::BigInt::GMP or Math::BigInt::Pari. 2445 2446 =head1 DESCRIPTION 2447 2448 In order to allow for multiple big integer libraries, Math::BigInt was 2449 rewritten to use library modules for core math routines. Any module which 2450 follows the same API as this can be used instead by using the following: 2451 2452 use Math::BigInt lib => 'libname'; 2453 2454 'libname' is either the long name ('Math::BigInt::Pari'), or only the short 2455 version like 'Pari'. 2456 2457 =head1 STORAGE 2458 2459 =head1 METHODS 2460 2461 The following functions MUST be defined in order to support the use by 2462 Math::BigInt v1.70 or later: 2463 2464 api_version() return API version, 1 for v1.70, 2 for v1.83 2465 _new(string) return ref to new object from ref to decimal string 2466 _zero() return a new object with value 0 2467 _one() return a new object with value 1 2468 _two() return a new object with value 2 2469 _ten() return a new object with value 10 2470 2471 _str(obj) return ref to a string representing the object 2472 _num(obj) returns a Perl integer/floating point number 2473 NOTE: because of Perl numeric notation defaults, 2474 the _num'ified obj may lose accuracy due to 2475 machine-dependent floating point size limitations 2476 2477 _add(obj,obj) Simple addition of two objects 2478 _mul(obj,obj) Multiplication of two objects 2479 _div(obj,obj) Division of the 1st object by the 2nd 2480 In list context, returns (result,remainder). 2481 NOTE: this is integer math, so no 2482 fractional part will be returned. 2483 The second operand will be not be 0, so no need to 2484 check for that. 2485 _sub(obj,obj) Simple subtraction of 1 object from another 2486 a third, optional parameter indicates that the params 2487 are swapped. In this case, the first param needs to 2488 be preserved, while you can destroy the second. 2489 sub (x,y,1) => return x - y and keep x intact! 2490 _dec(obj) decrement object by one (input is guaranteed to be > 0) 2491 _inc(obj) increment object by one 2492 2493 2494 _acmp(obj,obj) <=> operator for objects (return -1, 0 or 1) 2495 2496 _len(obj) returns count of the decimal digits of the object 2497 _digit(obj,n) returns the n'th decimal digit of object 2498 2499 _is_one(obj) return true if argument is 1 2500 _is_two(obj) return true if argument is 2 2501 _is_ten(obj) return true if argument is 10 2502 _is_zero(obj) return true if argument is 0 2503 _is_even(obj) return true if argument is even (0,2,4,6..) 2504 _is_odd(obj) return true if argument is odd (1,3,5,7..) 2505 2506 _copy return a ref to a true copy of the object 2507 2508 _check(obj) check whether internal representation is still intact 2509 return 0 for ok, otherwise error message as string 2510 2511 _from_hex(str) return new object from a hexadecimal string 2512 _from_bin(str) return new object from a binary string 2513 _from_oct(str) return new object from an octal string 2514 2515 _as_hex(str) return string containing the value as 2516 unsigned hex string, with the '0x' prepended. 2517 Leading zeros must be stripped. 2518 _as_bin(str) Like as_hex, only as binary string containing only 2519 zeros and ones. Leading zeros must be stripped and a 2520 '0b' must be prepended. 2521 2522 _rsft(obj,N,B) shift object in base B by N 'digits' right 2523 _lsft(obj,N,B) shift object in base B by N 'digits' left 2524 2525 _xor(obj1,obj2) XOR (bit-wise) object 1 with object 2 2526 Note: XOR, AND and OR pad with zeros if size mismatches 2527 _and(obj1,obj2) AND (bit-wise) object 1 with object 2 2528 _or(obj1,obj2) OR (bit-wise) object 1 with object 2 2529 2530 _mod(obj1,obj2) Return remainder of div of the 1st by the 2nd object 2531 _sqrt(obj) return the square root of object (truncated to int) 2532 _root(obj) return the n'th (n >= 3) root of obj (truncated to int) 2533 _fac(obj) return factorial of object 1 (1*2*3*4..) 2534 _pow(obj1,obj2) return object 1 to the power of object 2 2535 return undef for NaN 2536 _zeros(obj) return number of trailing decimal zeros 2537 _modinv return inverse modulus 2538 _modpow return modulus of power ($x ** $y) % $z 2539 _log_int(X,N) calculate integer log() of X in base N 2540 X >= 0, N >= 0 (return undef for NaN) 2541 returns (RESULT, EXACT) where EXACT is: 2542 1 : result is exactly RESULT 2543 0 : result was truncated to RESULT 2544 undef : unknown whether result is exactly RESULT 2545 _gcd(obj,obj) return Greatest Common Divisor of two objects 2546 2547 The following functions are REQUIRED for an api_version of 2 or greater: 2548 2549 _1ex($x) create the number 1Ex where x >= 0 2550 _alen(obj) returns approximate count of the decimal digits of the 2551 object. This estimate MUST always be greater or equal 2552 to what _len() returns. 2553 _nok(n,k) calculate n over k (binomial coefficient) 2554 2555 The following functions are optional, and can be defined if the underlying lib 2556 has a fast way to do them. If undefined, Math::BigInt will use pure Perl (hence 2557 slow) fallback routines to emulate these: 2558 2559 _signed_or 2560 _signed_and 2561 _signed_xor 2562 2563 Input strings come in as unsigned but with prefix (i.e. as '123', '0xabc' 2564 or '0b1101'). 2565 2566 So the library needs only to deal with unsigned big integers. Testing of input 2567 parameter validity is done by the caller, so you need not worry about 2568 underflow (f.i. in C<_sub()>, C<_dec()>) nor about division by zero or similar 2569 cases. 2570 2571 The first parameter can be modified, that includes the possibility that you 2572 return a reference to a completely different object instead. Although keeping 2573 the reference and just changing its contents is preferred over creating and 2574 returning a different reference. 2575 2576 Return values are always references to objects, strings, or true/false for 2577 comparison routines. 2578 2579 =head1 WRAP YOUR OWN 2580 2581 If you want to port your own favourite c-lib for big numbers to the 2582 Math::BigInt interface, you can take any of the already existing modules as 2583 a rough guideline. You should really wrap up the latest BigInt and BigFloat 2584 testsuites with your module, and replace in them any of the following: 2585 2586 use Math::BigInt; 2587 2588 by this: 2589 2590 use Math::BigInt lib => 'yourlib'; 2591 2592 This way you ensure that your library really works 100% within Math::BigInt. 2593 2594 =head1 LICENSE 2595 2596 This program is free software; you may redistribute it and/or modify it under 2597 the same terms as Perl itself. 2598 2599 =head1 AUTHORS 2600 2601 Original math code by Mark Biggar, rewritten by Tels L<http://bloodgate.com/> 2602 in late 2000. 2603 Seperated from BigInt and shaped API with the help of John Peacock. 2604 2605 Fixed, speed-up, streamlined and enhanced by Tels 2001 - 2007. 2606 2607 =head1 SEE ALSO 2608 2609 L<Math::BigInt>, L<Math::BigFloat>, 2610 L<Math::BigInt::GMP>, L<Math::BigInt::FastCalc> and L<Math::BigInt::Pari>. 2611 2612 =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 |