Move Math::BigInt from ext/ to cpan/
[p5sagit/p5-mst-13.2.git] / cpan / Math-BigInt / lib / Math / BigInt / Calc.pm
CommitLineData
0716bf9b 1package Math::BigInt::Calc;
2
08a3f4a9 3use 5.006;
0716bf9b 4use strict;
574bacfe 5# use warnings; # dont use warnings for older Perls
0716bf9b 6
08a3f4a9 7our $VERSION = '0.52';
0716bf9b 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
027dc388 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
0716bf9b 14
15# todo:
a87115f0 16# - fully remove funky $# stuff in div() (maybe - that code scares me...)
0716bf9b 17
18# USE_MUL: due to problems on certain os (os390, posix-bc) "* 1e-5" is used
ee15d750 19# instead of "/ 1e5" at some places, (marked with USE_MUL). Other platforms
20# BS2000, some Crays need USE_DIV instead.
bd05a461 21# The BEGIN block is used to determine which of the two variants gives the
22# correct result.
0716bf9b 23
990fb837 24# Beware of things like:
2ebb273f 25# $i = $i * $y + $car; $car = int($i / $BASE); $i = $i % $BASE;
93c87d9d 26# This works on x86, but fails on ARM (SA1100, iPAQ) due to whoknows what
990fb837 27# reasons. So, use this instead (slower, but correct):
2ebb273f 28# $i = $i * $y + $car; $car = int($i / $BASE); $i -= $BASE * $car;
990fb837 29
0716bf9b 30##############################################################################
31# global constants, flags and accessory
9b924220 32
2ebb273f 33# announce that we are compatible with MBI v1.83 and up
50109ad0 34sub api_version () { 2; }
0716bf9b 35
36# constants for easier life
2ebb273f 37my ($BASE,$BASE_LEN,$RBASE,$MAX_VAL);
394e6ffb 38my ($AND_BITS,$XOR_BITS,$OR_BITS);
39my ($AND_MASK,$XOR_MASK,$OR_MASK);
ee15d750 40
41sub _base_len
42 {
50109ad0 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:
394e6ffb 46 shift;
47
8d8376c6 48 my ($b, $int) = @_;
ee15d750 49 if (defined $b)
50 {
2ebb273f 51 # avoid redefinitions
52 undef &_mul;
53 undef &_div;
54
08a3f4a9 55 if ($] >= 5.008 && $int && $b > 7)
2ebb273f 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;
61f5c3f5 68 my $caught = 0;
2ebb273f 69 while (--$BASE_LEN > 5)
394e6ffb 70 {
2ebb273f 71 $BASE = int("1e".$BASE_LEN);
72 $RBASE = abs('1e-'.$BASE_LEN); # see USE_MUL
394e6ffb 73 $caught = 0;
2ebb273f 74 $caught += 1 if (int($BASE * $RBASE) != 1); # should be 1
75 $caught += 2 if (int($BASE / $BASE) != 1); # should be 1
394e6ffb 76 last if $caught != 3;
77 }
ee15d750 78 $BASE = int("1e".$BASE_LEN);
2ebb273f 79 $RBASE = abs('1e-'.$BASE_LEN); # see USE_MUL
80 $MAX_VAL = $BASE-1;
b68b7ab1 81
50109ad0 82 # ($caught & 1) != 0 => cannot use MUL
83 # ($caught & 2) != 0 => cannot use DIV
990fb837 84 if ($caught == 2) # 2
ee15d750 85 {
990fb837 86 # must USE_MUL since we cannot use DIV
2ebb273f 87 *_mul = \&_mul_use_mul;
88 *_div = \&_div_use_mul;
ee15d750 89 }
990fb837 90 else # 0 or 1
ee15d750 91 {
ee15d750 92 # can USE_DIV instead
2ebb273f 93 *_mul = \&_mul_use_div;
94 *_div = \&_div_use_div;
ee15d750 95 }
96 }
61f5c3f5 97 return $BASE_LEN unless wantarray;
2ebb273f 98 return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL, $BASE);
ee15d750 99 }
574bacfe 100
03874afe 101sub _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
574bacfe 116BEGIN
117 {
bd05a461 118 # from Daniel Pfeiffer: determine largest group of digits that is precisely
574bacfe 119 # multipliable with itself plus carry
dccbb853 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
bd05a461 122 do
123 {
124 $num = ('9' x ++$e) + 0;
394e6ffb 125 $num *= $num + 1.0;
394e6ffb 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)
2ebb273f 133
8d8376c6 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
394e6ffb 153
b68b7ab1 154 use integer;
394e6ffb 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.
394e6ffb 157 local $^W = 0; # don't warn about 'nonportable number'
c38b2de2 158 $AND_BITS = 15; $XOR_BITS = 15; $OR_BITS = 15;
394e6ffb 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++; }
1ddff52a 164 {
165 no integer;
166 $max = 16 if $] < 5.006; # older Perls might not take >16 too well
167 }
394e6ffb 168 my ($x,$y,$z);
169 do {
170 $AND_BITS++;
0617f807 171 $x = CORE::oct('0b' . '1' x $AND_BITS); $y = $x & $x;
394e6ffb 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++;
0617f807 177 $x = CORE::oct('0b' . '1' x $XOR_BITS); $y = $x ^ 0;
394e6ffb 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++;
0617f807 183 $x = CORE::oct('0b' . '1' x $OR_BITS); $y = $x | $x;
394e6ffb 184 $z = (2 ** $OR_BITS) - 1;
185 } while ($OR_BITS < $max && $x == $z && $y == $x);
186 $OR_BITS --; # retreat one step
187
9b924220 188 $AND_MASK = __PACKAGE__->_new( ( 2 ** $AND_BITS ));
189 $XOR_MASK = __PACKAGE__->_new( ( 2 ** $XOR_BITS ));
190 $OR_MASK = __PACKAGE__->_new( ( 2 ** $OR_BITS ));
50109ad0 191
192 # We can compute the approximate lenght no faster than the real length:
193 *_alen = \&_len;
394e6ffb 194 }
0716bf9b 195
03874afe 196###############################################################################
197
0716bf9b 198sub _zero
199 {
200 # create a zero
61f5c3f5 201 [ 0 ];
0716bf9b 202 }
203
204sub _one
205 {
206 # create a one
61f5c3f5 207 [ 1 ];
0716bf9b 208 }
209
027dc388 210sub _two
211 {
1ddff52a 212 # create a two (used internally for shifting)
61f5c3f5 213 [ 2 ];
027dc388 214 }
215
9b924220 216sub _ten
217 {
218 # create a 10 (used internally for shifting)
219 [ 10 ];
220 }
221
50109ad0 222sub _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
0716bf9b 232sub _copy
233 {
091c87b1 234 # make a true copy
61f5c3f5 235 [ @{$_[1]} ];
0716bf9b 236 }
237
bd05a461 238# catch and throw away
239sub import { }
240
0716bf9b 241##############################################################################
242# convert back to string and number
243
244sub _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")
574bacfe 249 my $ar = $_[1];
61f5c3f5 250
b68b7ab1 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 }
61f5c3f5 257
b68b7ab1 258 my $ret = "";
0716bf9b 259 # handle first one different to strip leading zeros from it (there are no
260 # leading zero parts in internal representation)
61f5c3f5 261 $l --; $ret .= int($ar->[$l]); $l--;
0716bf9b 262 # Interestingly, the pre-padd method uses more time
091c87b1 263 # the old grep variant takes longer (14 vs. 10 sec)
574bacfe 264 my $z = '0' x ($BASE_LEN-1);
0716bf9b 265 while ($l >= 0)
266 {
574bacfe 267 $ret .= substr($z.$ar->[$l],-$BASE_LEN); # fastest way I could think of
0716bf9b 268 $l--;
269 }
9b924220 270 $ret;
0716bf9b 271 }
272
273sub _num
274 {
9b924220 275 # Make a number (scalar int/float) from a BigInt object
574bacfe 276 my $x = $_[1];
9b924220 277
278 return 0+$x->[0] if scalar @$x == 1; # below $BASE
0716bf9b 279 my $fac = 1;
280 my $num = 0;
281 foreach (@$x)
282 {
283 $num += $fac*$_; $fac *= $BASE;
284 }
61f5c3f5 285 $num;
0716bf9b 286 }
287
288##############################################################################
289# actual math code
290
291sub _add
292 {
293 # (ref to int_num_array, ref to int_num_array)
574bacfe 294 # routine to add two base 1eX numbers
0716bf9b 295 # stolen from Knuth Vol 2 Algorithm A pg 231
b22b3e31 296 # there are separate routines to add and sub as per Knuth pg 233
0716bf9b 297 # This routine clobbers up array x, but not y.
298
574bacfe 299 my ($c,$x,$y) = @_;
b3abae2a 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 {
7b29e1e6 304 # twice as slow as $x = [ @$y ], but nec. to retain $x as ref :(
b3abae2a 305 @$x = @$y; return $x;
306 }
0716bf9b 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
091c87b1 310 # Trades one "$j++" for having to shift arrays
0716bf9b 311 my $i; my $car = 0; my $j = 0;
312 for $i (@$y)
313 {
e745a66c 314 $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0;
0716bf9b 315 $j++;
316 }
317 while ($car != 0)
318 {
319 $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0; $j++;
320 }
61f5c3f5 321 $x;
e745a66c 322 }
323
324sub _inc
325 {
326 # (ref to int_num_array, ref to int_num_array)
091c87b1 327 # Add 1 to $x, modify $x in place
e745a66c 328 my ($c,$x) = @_;
329
330 for my $i (@$x)
331 {
332 return $x if (($i += 1) < $BASE); # early out
61f5c3f5 333 $i = 0; # overflow, next
e745a66c 334 }
ae161977 335 push @$x,1 if (($x->[-1] || 0) == 0); # last overflowed, so extend
61f5c3f5 336 $x;
e745a66c 337 }
338
339sub _dec
340 {
341 # (ref to int_num_array, ref to int_num_array)
091c87b1 342 # Sub 1 from $x, modify $x in place
e745a66c 343 my ($c,$x) = @_;
344
2ebb273f 345 my $MAX = $BASE-1; # since MAX_VAL based on BASE
e745a66c 346 for my $i (@$x)
347 {
348 last if (($i -= 1) >= 0); # early out
091c87b1 349 $i = $MAX; # underflow, next
e745a66c 350 }
091c87b1 351 pop @$x if $x->[-1] == 0 && @$x > 1; # last underflowed (but leave 0)
61f5c3f5 352 $x;
0716bf9b 353 }
354
355sub _sub
356 {
9393ace2 357 # (ref to int_num_array, ref to int_num_array, swap)
574bacfe 358 # subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
56b9c951 359 # subtract Y from X by modifying x in place
574bacfe 360 my ($c,$sx,$sy,$s) = @_;
0716bf9b 361
362 my $car = 0; my $i; my $j = 0;
363 if (!$s)
364 {
0716bf9b 365 for $i (@$sx)
366 {
367 last unless defined $sy->[$j] || $car;
0716bf9b 368 $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0); $j++;
0716bf9b 369 }
370 # might leave leading zeros, so fix that
394e6ffb 371 return __strip_zeros($sx);
0716bf9b 372 }
394e6ffb 373 for $i (@$sx)
0716bf9b 374 {
07d34614 375 # we can't do an early out if $x is < than $y, since we
56b9c951 376 # need to copy the high chunks from $y. Found by Bob Mathews.
377 #last unless defined $sy->[$j] || $car;
394e6ffb 378 $sy->[$j] += $BASE
379 if $car = (($sy->[$j] = $i-($sy->[$j]||0) - $car) < 0);
380 $j++;
0716bf9b 381 }
394e6ffb 382 # might leave leading zeros, so fix that
383 __strip_zeros($sy);
0716bf9b 384 }
385
ee15d750 386sub _mul_use_mul
0716bf9b 387 {
9393ace2 388 # (ref to int_num_array, ref to int_num_array)
0716bf9b 389 # multiply two numbers in internal representation
b22b3e31 390 # modifies first arg, second need not be different from first
574bacfe 391 my ($c,$xv,$yv) = @_;
dccbb853 392
990fb837 393 if (@$yv == 1)
b3abae2a 394 {
990fb837 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 {
2ebb273f 399 if (($xv->[0] *= $yv->[0]) >= $BASE)
990fb837 400 {
2ebb273f 401 $xv->[0] = $xv->[0] - ($xv->[1] = int($xv->[0] * $RBASE)) * $BASE;
990fb837 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 {
2ebb273f 415 $i = $i * $y + $car; $car = int($i * $RBASE); $i -= $car * $BASE;
990fb837 416 }
417 push @$xv, $car if $car != 0;
b3abae2a 418 return $xv;
419 }
990fb837 420 # shortcut for result $x == 0 => result = 0
421 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) );
b3abae2a 422
0716bf9b 423 # since multiplying $x with $x fails, make copy in this case
d614cd8b 424 $yv = [@$xv] if $xv == $yv; # same references?
9393ace2 425
61f5c3f5 426 my @prod = (); my ($prod,$car,$cty,$xi,$yi);
427
0716bf9b 428 for $xi (@$xv)
429 {
430 $car = 0; $cty = 0;
574bacfe 431
432 # slow variant
433# for $yi (@$yv)
434# {
435# $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
436# $prod[$cty++] =
2ebb273f 437# $prod - ($car = int($prod * RBASE)) * $BASE; # see USE_MUL
574bacfe 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;
0716bf9b 445 for $yi (@$yv)
446 {
447 $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
574bacfe 448## this is actually a tad slower
449## $prod = $prod[$cty]; $prod += ($car + $xi * $yi); # no ||0 here
0716bf9b 450 $prod[$cty++] =
2ebb273f 451 $prod - ($car = int($prod * $RBASE)) * $BASE; # see USE_MUL
0716bf9b 452 }
453 $prod[$cty] += $car if $car; # need really to check for 0?
027dc388 454 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy
0716bf9b 455 }
0716bf9b 456 push @$xv, @prod;
50109ad0 457 # can't have leading zeros
458# __strip_zeros($xv);
61f5c3f5 459 $xv;
0716bf9b 460 }
461
2ebb273f 462sub _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
ee15d750 525sub _mul_use_div
526 {
9393ace2 527 # (ref to int_num_array, ref to int_num_array)
ee15d750 528 # multiply two numbers in internal representation
529 # modifies first arg, second need not be different from first
530 my ($c,$xv,$yv) = @_;
50109ad0 531
990fb837 532 if (@$yv == 1)
b3abae2a 533 {
990fb837 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
2ebb273f 539 if (($xv->[0] *= $yv->[0]) >= $BASE)
990fb837 540 {
541 $xv->[0] =
2ebb273f 542 $xv->[0] - ($xv->[1] = int($xv->[0] / $BASE)) * $BASE;
990fb837 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 {
2ebb273f 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;
990fb837 559 }
560 push @$xv, $car if $car != 0;
b3abae2a 561 return $xv;
562 }
990fb837 563 # shortcut for result $x == 0 => result = 0
564 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) );
b3abae2a 565
ee15d750 566 # since multiplying $x with $x fails, make copy in this case
d614cd8b 567 $yv = [@$xv] if $xv == $yv; # same references?
9393ace2 568
61f5c3f5 569 my @prod = (); my ($prod,$car,$cty,$xi,$yi);
ee15d750 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;
2ebb273f 578 $prod[$cty++] = $prod - ($car = int($prod / $BASE)) * $BASE;
ee15d750 579 }
580 $prod[$cty] += $car if $car; # need really to check for 0?
027dc388 581 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy
ee15d750 582 }
583 push @$xv, @prod;
50109ad0 584 # can't have leading zeros
585# __strip_zeros($xv);
61f5c3f5 586 $xv;
ee15d750 587 }
588
589sub _div_use_mul
0716bf9b 590 {
b22b3e31 591 # ref to array, ref to array, modify first array and return remainder if
0716bf9b 592 # in list context
aef458a0 593
594 # see comments in _div_use_div() for more explanations
595
574bacfe 596 my ($c,$x,$yorg) = @_;
aef458a0 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.
0716bf9b 600
aef458a0 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:
61f5c3f5 605 if (@$x == 1 && @$yorg == 1)
606 {
13a12e00 607 # shortcut, $yorg and $x are two small numbers
61f5c3f5 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 }
aef458a0 620
621 # if x has more than one, but y has only one element:
28df3e88 622 if (@$yorg == 1)
623 {
624 my $rem;
625 $rem = _mod($c,[ @$x ],$yorg) if wantarray;
13a12e00 626
28df3e88 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 {
2ebb273f 632 $b = $r * $BASE + $x->[$j];
28df3e88 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 }
0716bf9b 640
aef458a0 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 {
b68b7ab1 671 # same length, so make full compare
672
aef458a0 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
b68b7ab1 680 # a < 0: x < y, a == 0: x == y, a > 0: x > y
aef458a0 681 if ($a <= 0)
682 {
b68b7ab1 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
7596a890 688 return ($x,$rem) if wantarray;
689 return $x;
aef458a0 690 }
b68b7ab1 691 # $x >= $y, so proceed normally
aef458a0 692 }
693 }
694
695 # all other cases:
696
d614cd8b 697 my $y = [ @$yorg ]; # always make copy to preserve
61f5c3f5 698
699 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);
700
701 $car = $bar = $prd = 0;
2ebb273f 702 if (($dd = int($BASE/($y->[-1]+1))) != 1)
0716bf9b 703 {
704 for $xi (@$x)
705 {
706 $xi = $xi * $dd + $car;
2ebb273f 707 $xi -= ($car = int($xi * $RBASE)) * $BASE; # see USE_MUL
0716bf9b 708 }
709 push(@$x, $car); $car = 0;
710 for $yi (@$y)
711 {
712 $yi = $yi * $dd + $car;
2ebb273f 713 $yi -= ($car = int($yi * $RBASE)) * $BASE; # see USE_MUL
0716bf9b 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;
2ebb273f 728 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$BASE+$u1)/$v1));
729 --$q while ($v2*$q > ($u0*$BASE+$u1-$q*$v1)*$BASE+$u2);
0716bf9b 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;
2ebb273f 736 $prd -= ($car = int($prd * $RBASE)) * $BASE; # see USE_MUL
737 $x->[$xi] += $BASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0));
0716bf9b 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 {
2ebb273f 744 $x->[$xi] -= $BASE
745 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE));
0716bf9b 746 }
747 }
748 }
aef458a0 749 pop(@$x);
750 unshift(@q, $q);
0716bf9b 751 }
752 if (wantarray)
753 {
754 @d = ();
755 if ($dd != 1)
756 {
757 $car = 0;
758 for $xi (reverse @$x)
759 {
2ebb273f 760 $prd = $car * $BASE + $xi;
0716bf9b 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;
61f5c3f5 770 my $d = \@d;
990fb837 771 __strip_zeros($x);
772 __strip_zeros($d);
61f5c3f5 773 return ($x,$d);
0716bf9b 774 }
775 @$x = @q;
990fb837 776 __strip_zeros($x);
61f5c3f5 777 $x;
0716bf9b 778 }
779
2ebb273f 780sub _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
ee15d750 973sub _div_use_div
974 {
975 # ref to array, ref to array, modify first array and return remainder if
976 # in list context
ee15d750 977 my ($c,$x,$yorg) = @_;
ee15d750 978
990fb837 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:
61f5c3f5 986 if (@$x == 1 && @$yorg == 1)
987 {
13a12e00 988 # shortcut, $yorg and $x are two small numbers
61f5c3f5 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 }
990fb837 1001 # if x has more than one, but y has only one element:
28df3e88 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 {
2ebb273f 1012 $b = $r * $BASE + $x->[$j];
28df3e88 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 }
990fb837 1020 # now x and y have more than one element
ee15d750 1021
990fb837 1022 # check whether y has more elements than x, if yet, the result will be 0
1023 if (@$yorg > @$x)
61f5c3f5 1024 {
990fb837 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?
aef458a0 1030 return $x; # only x, which is [0] now
61f5c3f5 1031 }
990fb837 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
091c87b1 1048
990fb837 1049 if (length(int($yorg->[-1])) == length(int($x->[-1])))
1050 {
b68b7ab1 1051 # same length, so make full compare
1052
990fb837 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 }
aef458a0 1059 # $a contains the result of the compare between X and Y
b68b7ab1 1060 # a < 0: x < y, a == 0: x == y, a > 0: x > y
990fb837 1061 if ($a <= 0)
1062 {
b68b7ab1 1063 $rem = [ 0 ]; # a = 0 => x == y => rem 0
1064 $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x
aef458a0 1065 splice(@$x,1); # keep single element
990fb837 1066 $x->[0] = 0; # if $a < 0
b68b7ab1 1067 $x->[0] = 1 if $a == 0; # $x == $y
7596a890 1068 return ($x,$rem) if wantarray; # including remainder?
1069 return $x;
990fb837 1070 }
aef458a0 1071 # $x >= $y, so proceed normally
b68b7ab1 1072
990fb837 1073 }
990fb837 1074 }
1075
1076 # all other cases:
1077
1078 my $y = [ @$yorg ]; # always make copy to preserve
61f5c3f5 1079
1080 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);
1081
1082 $car = $bar = $prd = 0;
2ebb273f 1083 if (($dd = int($BASE/($y->[-1]+1))) != 1)
ee15d750 1084 {
1085 for $xi (@$x)
1086 {
1087 $xi = $xi * $dd + $car;
2ebb273f 1088 $xi -= ($car = int($xi / $BASE)) * $BASE;
ee15d750 1089 }
1090 push(@$x, $car); $car = 0;
1091 for $yi (@$y)
1092 {
1093 $yi = $yi * $dd + $car;
2ebb273f 1094 $yi -= ($car = int($yi / $BASE)) * $BASE;
ee15d750 1095 }
1096 }
1097 else
1098 {
1099 push(@$x, 0);
1100 }
aef458a0 1101
1102 # @q will accumulate the final result, $q contains the current computed
1103 # part of the final result
1104
ee15d750 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;
2ebb273f 1113 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$BASE+$u1)/$v1));
1114 --$q while ($v2*$q > ($u0*$BASE+$u1-$q*$v1)*$BASE+$u2);
ee15d750 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;
2ebb273f 1121 $prd -= ($car = int($prd / $BASE)) * $BASE;
1122 $x->[$xi] += $BASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0));
ee15d750 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 {
2ebb273f 1129 $x->[$xi] -= $BASE
1130 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE));
ee15d750 1131 }
1132 }
1133 }
61f5c3f5 1134 pop(@$x); unshift(@q, $q);
ee15d750 1135 }
1136 if (wantarray)
1137 {
1138 @d = ();
1139 if ($dd != 1)
1140 {
1141 $car = 0;
1142 for $xi (reverse @$x)
1143 {
2ebb273f 1144 $prd = $car * $BASE + $xi;
ee15d750 1145 $car = $prd - ($tmp = int($prd / $dd)) * $dd;
1146 unshift(@d, $tmp);
1147 }
1148 }
1149 else
1150 {
1151 @d = @$x;
1152 }
1153 @$x = @q;
61f5c3f5 1154 my $d = \@d;
990fb837 1155 __strip_zeros($x);
1156 __strip_zeros($d);
61f5c3f5 1157 return ($x,$d);
ee15d750 1158 }
1159 @$x = @q;
990fb837 1160 __strip_zeros($x);
61f5c3f5 1161 $x;
ee15d750 1162 }
1163
394e6ffb 1164##############################################################################
1165# testing
1166
1167sub _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
394e6ffb 1172 my ($c,$cx,$cy) = @_;
091c87b1 1173
1174 # shortcut for short numbers
1175 return (($cx->[0] <=> $cy->[0]) <=> 0)
1176 if scalar @$cx == scalar @$cy && scalar @$cx == 1;
394e6ffb 1177
f9a08e12 1178 # fast comp based on number of array elements (aka pseudo-length)
091c87b1 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])));
394e6ffb 1184 return -1 if $lxy < 0; # already differs, ret
1185 return 1 if $lxy > 0; # ditto
56d9de68 1186
394e6ffb 1187 # manual way (abort if unequal, good for early ne)
091c87b1 1188 my $a; my $j = scalar @$cx;
1189 while (--$j >= 0)
9393ace2 1190 {
091c87b1 1191 last if ($a = $cx->[$j] - $cy->[$j]);
9393ace2 1192 }
091c87b1 1193 $a <=> 0;
394e6ffb 1194 }
1195
1196sub _len
1197 {
50109ad0 1198 # compute number of digits in base 10
394e6ffb 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
56d9de68 1204 (@$cx-1)*$BASE_LEN+length(int($cx->[-1]));
394e6ffb 1205 }
1206
1207sub _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
52edfb59 1221 $elem = '0' x $BASE_LEN . @$x[$elem]; # get element padded with 0's
56d9de68 1222 substr($elem,-$digit-1,1);
394e6ffb 1223 }
1224
1225sub _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];
9b924220 1231
1232 return 0 if scalar @$x == 1 && $x->[0] == 0;
1233
394e6ffb 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
61f5c3f5 1242 $zeros += length($elem); # count trailing zeros
394e6ffb 1243 last; # early out
1244 }
1245 $zeros ++; # real else branch: 50% slower!
1246 }
61f5c3f5 1247 $zeros;
394e6ffb 1248 }
1249
1250##############################################################################
1251# _is_* routines
1252
1253sub _is_zero
1254 {
9b924220 1255 # return true if arg is zero
1256 (((scalar @{$_[1]} == 1) && ($_[1]->[0] == 0))) <=> 0;
394e6ffb 1257 }
1258
1259sub _is_even
1260 {
9b924220 1261 # return true if arg is even
1262 (!($_[1]->[0] & 1)) <=> 0;
394e6ffb 1263 }
1264
1265sub _is_odd
1266 {
9b924220 1267 # return true if arg is even
1268 (($_[1]->[0] & 1)) <=> 0;
394e6ffb 1269 }
1270
1271sub _is_one
1272 {
9b924220 1273 # return true if arg is one
1274 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 1) <=> 0;
1275 }
1276
1277sub _is_two
1278 {
1279 # return true if arg is two
1280 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 2) <=> 0;
1281 }
61f5c3f5 1282
9b924220 1283sub _is_ten
1284 {
1285 # return true if arg is ten
1286 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 10) <=> 0;
394e6ffb 1287 }
1288
1289sub __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
61f5c3f5 1299 return $s if @$s == 1; # early out
1300
394e6ffb 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###############################################################################
3a427a11 1315# check routine to test internal state for corruptions
394e6ffb 1316
1317sub _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;
3a427a11 1344 0;
394e6ffb 1345 }
1346
1347
1348###############################################################################
394e6ffb 1349
dccbb853 1350sub _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 }
aef458a0 1361
dccbb853 1362 my $y = $yo->[0];
027dc388 1363 # both are single element arrays
dccbb853 1364 if (scalar @$x == 1)
1365 {
1366 $x->[0] %= $y;
1367 return $x;
1368 }
1369
aef458a0 1370 # @y is a single element, but @x has more than one element
dccbb853 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 }
027dc388 1379 elsif ($b == 1)
1380 {
3a427a11 1381 # else need to go through all elements: O(N), but loop is a bit simplified
027dc388 1382 my $r = 0;
1383 foreach (@$x)
1384 {
28df3e88 1385 $r = ($r + $_) % $y; # not much faster, but heh...
1386 #$r += $_ % $y; $r %= $y;
027dc388 1387 }
1388 $r = 0 if $r == $y;
1389 $x->[0] = $r;
1390 }
dccbb853 1391 else
1392 {
3a427a11 1393 # else need to go through all elements: O(N)
027dc388 1394 my $r = 0; my $bm = 1;
1395 foreach (@$x)
1396 {
28df3e88 1397 $r = ($_ * $bm + $r) % $y;
1398 $bm = ($bm * $b) % $y;
1399
1400 #$r += ($_ % $y) * $bm;
1401 #$bm *= $b;
1402 #$bm %= $y;
1403 #$r %= $y;
027dc388 1404 }
1405 $r = 0 if $r == $y;
1406 $x->[0] = $r;
dccbb853 1407 }
091c87b1 1408 splice (@$x,1); # keep one element of $x
61f5c3f5 1409 $x;
dccbb853 1410 }
1411
0716bf9b 1412##############################################################################
574bacfe 1413# shifts
1414
1415sub _rsft
1416 {
1417 my ($c,$x,$y,$n) = @_;
1418
1419 if ($n != 10)
1420 {
9b924220 1421 $n = _new($c,$n); return _div($c,$x, _pow($c,$n,$y));
61f5c3f5 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
56d9de68 1428 my $xlen = (@$x-1)*$BASE_LEN+length(int($x->[-1])); # len of x in digits
90d1b129 1429 if ($src >= $xlen or ($src == $xlen and ! defined $x->[1]))
56d9de68 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 }
61f5c3f5 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
574bacfe 1441 }
1442 else
1443 {
61f5c3f5 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)
574bacfe 1448 {
61f5c3f5 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++;
574bacfe 1456 }
61f5c3f5 1457 splice (@$x,$dst) if $dst > 0; # kill left-over array elems
56b9c951 1458 pop @$x if $x->[-1] == 0 && @$x > 1; # kill last element if 0
61f5c3f5 1459 } # else rem == 0
574bacfe 1460 $x;
1461 }
1462
1463sub _lsft
1464 {
1465 my ($c,$x,$y,$n) = @_;
1466
1467 if ($n != 10)
1468 {
9b924220 1469 $n = _new($c,$n); return _mul($c,$x, _pow($c,$n,$y));
574bacfe 1470 }
61f5c3f5 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)
574bacfe 1482 {
61f5c3f5 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--;
574bacfe 1489 }
61f5c3f5 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;
574bacfe 1494 $x;
1495 }
1496
027dc388 1497sub _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
b282a552 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
027dc388 1519 my $pow2 = _one();
1ddff52a 1520
9b924220 1521 my $y_bin = _as_bin($c,$cy); $y_bin =~ s/^0b//;
1ddff52a 1522 my $len = length($y_bin);
1523 while (--$len > 0)
027dc388 1524 {
1ddff52a 1525 _mul($c,$pow2,$cx) if substr($y_bin,$len,1) eq '1'; # is odd?
027dc388 1526 _mul($c,$cx,$cx);
1527 }
1ddff52a 1528
1529 _mul($c,$cx,$pow2);
61f5c3f5 1530 $cx;
027dc388 1531 }
1532
50109ad0 1533sub _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
1568my @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
b3abae2a 1579sub _fac
1580 {
1581 # factorial of $x
1582 # ref to array, return ref to array
1583 my ($c,$cx) = @_;
1584
50109ad0 1585 if ((@$cx == 1) && ($cx->[0] <= 7))
b3abae2a 1586 {
50109ad0 1587 $cx->[0] = $factorials[$cx->[0]]; # 0 => 1, 1 => 1, 2 => 2 etc.
b3abae2a 1588 return $cx;
1589 }
1590
50109ad0 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
b3abae2a 1660 # go forward until $base is exceeded
091c87b1 1661 # limit is either $x steps (steps == 100 means a result always too high) or
1662 # $base.
b3abae2a 1663 my $steps = 100; $steps = $cx->[0] if @$cx == 1;
091c87b1 1664 my $r = 2; my $cf = 3; my $step = 2; my $last = $r;
1665 while ($r*$cf < $BASE && $step < $steps)
b3abae2a 1666 {
1667 $last = $r; $r *= $cf++; $step++;
1668 }
091c87b1 1669 if ((@$cx == 1) && $step == $cx->[0])
b3abae2a 1670 {
091c87b1 1671 # completely done, so keep reference to $x and return
1672 $cx->[0] = $r;
b3abae2a 1673 return $cx;
1674 }
091c87b1 1675
990fb837 1676 # now we must do the left over steps
091c87b1 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 }
b3abae2a 1686
50109ad0 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.
990fb837 1692 my $zero_elements = 0;
091c87b1 1693
1694 # do left-over steps fit into a scalar?
1695 if (ref $n eq 'ARRAY')
b3abae2a 1696 {
091c87b1 1697 # No, so use slower inc() & cmp()
50109ad0 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 }
091c87b1 1710 $step = [$step];
50109ad0 1711 while (_acmp($c,$step,$n) <= 0)
990fb837 1712 {
1713 if ($cx->[0] == 0)
1714 {
1715 $zero_elements ++; shift @$cx;
1716 }
091c87b1 1717 _mul($c,$cx,$step); _inc($c,$step);
990fb837 1718 }
990fb837 1719 }
091c87b1 1720 else
990fb837 1721 {
091c87b1 1722 # Yes, so we can speed it up slightly
50109ad0 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)
990fb837 1730 {
091c87b1 1731 if ($cx->[0] == 0)
1732 {
1733 $zero_elements ++; shift @$cx;
1734 }
50109ad0 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 {
091c87b1 1753 _mul($c,$cx,[$step]); $step++;
50109ad0 1754 if ($cx->[0] == 0)
1755 {
1756 $zero_elements ++; shift @$cx;
1757 }
990fb837 1758 }
990fb837 1759 }
1760 # multiply in the zeros again
50109ad0 1761 unshift @$cx, (0) x $zero_elements;
091c87b1 1762 $cx; # return result
1763 }
1764
9b924220 1765#############################################################################
1766
091c87b1 1767sub _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);
b282a552 1777 my $cmp = _acmp($c,$x,$base); # X == BASE => 1
091c87b1 1778 if ($cmp == 0)
1779 {
1780 splice (@$x,1); $x->[0] = 1;
8df1e0a2 1781 return ($x,1)
091c87b1 1782 }
1783 # X < BASE
1784 if ($cmp < 0)
1785 {
1786 splice (@$x,1); $x->[0] = 0;
8df1e0a2 1787 return ($x,undef);
091c87b1 1788 }
1789
091c87b1 1790 my $x_org = _copy($c,$x); # preserve x
8df1e0a2 1791 splice(@$x,1); $x->[0] = 1; # keep ref to $x
1792
86b76201 1793 # Compute a guess for the result based on:
1794 # $guess = int ( length_in_base_10(X) / ( log(base) / log(10) ) )
2ebb273f 1795 my $len = _len($c,$x_org);
1796 my $log = log($base->[-1]) / log(10);
b282a552 1797
2ebb273f 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;
b282a552 1802
2ebb273f 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)
b282a552 1820 {
2ebb273f 1821# print STDERR "# big _log_int at ", _str($c,$x), "\n";
1822 _div($c,$trial,$base); _dec($c, $x);
b282a552 1823 }
2ebb273f 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
091c87b1 1847
2ebb273f 1848 my $base_mul = _mul($c, _copy($c,$base), $base); # $base * $base
8df1e0a2 1849
9b924220 1850 while (($a = _acmp($c,$trial,$x_org)) < 0)
091c87b1 1851 {
2ebb273f 1852# print STDERR "# small _log_int at ", _str($c,$x), "\n";
8df1e0a2 1853 _mul($c,$trial,$base_mul); _add($c, $x, [2]);
091c87b1 1854 }
8df1e0a2 1855
1856 my $exact = 1;
1857 if ($a > 0)
091c87b1 1858 {
8df1e0a2 1859 # overstepped the result
1860 _dec($c, $x);
1861 _div($c,$trial,$base);
9b924220 1862 $a = _acmp($c,$trial,$x_org);
8df1e0a2 1863 if ($a > 0)
091c87b1 1864 {
8df1e0a2 1865 _dec($c, $x);
091c87b1 1866 }
2ebb273f 1867 $exact = 0 if $a != 0; # a = -1 => not exact result, a = 0 => exact
091c87b1 1868 }
1869
8df1e0a2 1870 ($x,$exact); # return result
b3abae2a 1871 }
1872
56d9de68 1873# for debugging:
1874 use constant DEBUG => 0;
1875 my $steps = 0;
1876 sub steps { $steps };
b3abae2a 1877
1878sub _sqrt
0716bf9b 1879 {
56d9de68 1880 # square-root of $x in place
990fb837 1881 # Compute a guess of the result (by rule of thumb), then improve it via
56d9de68 1882 # Newton's method.
394e6ffb 1883 my ($c,$x) = @_;
0716bf9b 1884
394e6ffb 1885 if (scalar @$x == 1)
1886 {
50109ad0 1887 # fits into one Perl scalar, so result can be computed directly
394e6ffb 1888 $x->[0] = int(sqrt($x->[0]));
1889 return $x;
1890 }
1891 my $y = _copy($c,$x);
b3abae2a 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
56d9de68 1896 my $lastelem = $x->[-1]; # for guess
b3abae2a 1897 my $elems = scalar @$x - 1;
1898 # not enough digits, but could have more?
56d9de68 1899 if ((length($lastelem) <= 3) && ($elems > 1))
b3abae2a 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
56d9de68 1906 $lastelem = $lastelem / 10 if (length($lastelem) & 1) != $len;
b3abae2a 1907 print "$lastelem\n" if DEBUG;
1908 }
0716bf9b 1909
61f5c3f5 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);
b3abae2a 1913 print "l = $l " if DEBUG;
56d9de68 1914
1915 splice @$x,$l; # keep ref($x), but modify it
1916
b3abae2a 1917 # we make the first part of the guess not '1000...0' but int(sqrt($lastelem))
1918 # that gives us:
56d9de68 1919 # 14400 00000 => sqrt(14400) => guess first digits to be 120
1920 # 144000 000000 => sqrt(144000) => guess 379
b3abae2a 1921
b3abae2a 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;
56d9de68 1931
b3abae2a 1932 # If @$x > 1, we could compute the second elem of the guess, too, to create
56d9de68 1933 # an even better guess. Not implemented yet. Does it improve performance?
b3abae2a 1934 $x->[$l--] = 0 while ($l >= 0); # all other digits of guess are zero
56d9de68 1935
9b924220 1936 print "start x= ",_str($c,$x),"\n" if DEBUG;
394e6ffb 1937 my $two = _two();
1938 my $last = _zero();
1939 my $lastlast = _zero();
b3abae2a 1940 $steps = 0 if DEBUG;
394e6ffb 1941 while (_acmp($c,$last,$x) != 0 && _acmp($c,$lastlast,$x) != 0)
1942 {
b3abae2a 1943 $steps++ if DEBUG;
394e6ffb 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 );
9b924220 1948 print " x= ",_str($c,$x),"\n" if DEBUG;
394e6ffb 1949 }
b3abae2a 1950 print "\nsteps in sqrt: $steps, " if DEBUG;
394e6ffb 1951 _dec($c,$x) if _acmp($c,$y,_mul($c,_copy($c,$x),$x)) < 0; # overshot?
b3abae2a 1952 print " final ",$x->[-1],"\n" if DEBUG;
394e6ffb 1953 $x;
0716bf9b 1954 }
1955
990fb837 1956sub _root
1957 {
1958 # take n'th root of $x in place (n >= 3)
990fb837 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 {
50109ad0 1970 # fits into one Perl scalar, so result can be computed directly
091c87b1 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]) ) );
990fb837 1976 }
1977 return $x;
1978 }
1979
3a427a11 1980 # we know now that X is more than one element long
1981
c38b2de2 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);
9b924220 1985 if ($b =~ /0b1(0+)$/)
c38b2de2 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 {
091c87b1 2006 # trial computation by starting with 2,4,8,16 etc until we overstep
3a427a11 2007 my $step;
091c87b1 2008 my $trial = _two();
2009
3a427a11 2010 # while still to do more than X steps
2011 do
091c87b1 2012 {
3a427a11 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);
091c87b1 2029
3a427a11 2030 # reset step to 2
2031 $step = _two();
091c87b1 2032 # add two, because $trial cannot be exactly the result (otherwise we would
2033 # alrady have found it)
2034 _add($c, $trial, $step);
2035
3a427a11 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 }
091c87b1 2041
2042 # hit not exactly? (overstepped)
091c87b1 2043 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0)
2044 {
2045 _dec($c,$trial);
2046 }
3a427a11 2047
2048 # hit not exactly? (overstepped)
2049 # 80 too small, 81 slightly too big, 82 too big
091c87b1 2050 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0)
2051 {
3a427a11 2052 _dec ($c, $trial);
091c87b1 2053 }
3a427a11 2054
091c87b1 2055 @$x = @$trial; # make copy while preserving ref to $x
2056 return $x;
c38b2de2 2057 }
990fb837 2058 $x;
2059 }
2060
394e6ffb 2061##############################################################################
2062# binary stuff
0716bf9b 2063
394e6ffb 2064sub _and
2065 {
2066 my ($c,$x,$y) = @_;
0716bf9b 2067
394e6ffb 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
0716bf9b 2072
394e6ffb 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
9b924220 2087 # this is when the AND_BITS are greater than $BASE and is slower for
394e6ffb 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; }
9b924220 2093# _add($c,$x, _mul($c, _new( $c, ($xrr & $yrr) ), $m) );
394e6ffb 2094
61f5c3f5 2095 # 0+ due to '&' doesn't work in strings
2096 _add($c,$x, _mul($c, [ 0+$xr->[0] & 0+$yr->[0] ], $m) );
394e6ffb 2097 _mul($c,$m,$mask);
2098 }
2099 $x;
0716bf9b 2100 }
2101
394e6ffb 2102sub _xor
0716bf9b 2103 {
394e6ffb 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))
0716bf9b 2117 {
394e6ffb 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; }
9b924220 2123 #_add($c,$x, _mul($c, _new( $c, ($xrr ^ $yrr) ), $m) );
61f5c3f5 2124
2125 # 0+ due to '^' doesn't work in strings
2126 _add($c,$x, _mul($c, [ 0+$xr->[0] ^ 0+$yr->[0] ], $m) );
394e6ffb 2127 _mul($c,$m,$mask);
0716bf9b 2128 }
394e6ffb 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;
0716bf9b 2136 }
2137
394e6ffb 2138sub _or
0716bf9b 2139 {
394e6ffb 2140 my ($c,$x,$y) = @_;
0716bf9b 2141
394e6ffb 2142 return $x if _acmp($c,$x,$y) == 0; # shortcut (see _and)
0716bf9b 2143
394e6ffb 2144 my $m = _one(); my ($xr,$yr);
2145 my $mask = $OR_MASK;
0716bf9b 2146
394e6ffb 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; }
9b924220 2159# _add($c,$x, _mul($c, _new( $c, ($xrr | $yrr) ), $m) );
394e6ffb 2160
61f5c3f5 2161 # 0+ due to '|' doesn't work in strings
2162 _add($c,$x, _mul($c, [ 0+$xr->[0] | 0+$yr->[0] ], $m) );
394e6ffb 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;
0716bf9b 2172 }
2173
61f5c3f5 2174sub _as_hex
2175 {
2176 # convert a decimal number to hex (ref to array, return ref to string)
2177 my ($c,$x) = @_;
2178
50109ad0 2179 # fits into one element (handle also 0x0 case)
03874afe 2180 return sprintf("0x%x",$x->[0]) if @$x == 1;
990fb837 2181
61f5c3f5 2182 my $x1 = _copy($c,$x);
2183
2184 my $es = '';
1ddff52a 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 }
091c87b1 2194 while (@$x1 != 1 || $x1->[0] != 0) # _is_zero()
61f5c3f5 2195 {
2196 ($x1, $xr) = _div($c,$x1,$x10000);
7b29e1e6 2197 $es .= unpack($h,pack('V',$xr->[0]));
61f5c3f5 2198 }
2199 $es = reverse $es;
2200 $es =~ s/^[0]+//; # strip leading zeros
03874afe 2201 '0x' . $es; # return result prepended with 0x
61f5c3f5 2202 }
2203
2204sub _as_bin
2205 {
2206 # convert a decimal number to bin (ref to array, return ref to string)
2207 my ($c,$x) = @_;
2208
50109ad0 2209 # fits into one element (and Perl recent enough), handle also 0b0 case
091c87b1 2210 # handle zero case for older Perls
2211 if ($] <= 5.005 && @$x == 1 && $x->[0] == 0)
2212 {
9b924220 2213 my $t = '0b0'; return $t;
091c87b1 2214 }
2215 if (@$x == 1 && $] >= 5.006)
990fb837 2216 {
091c87b1 2217 my $t = sprintf("0b%b",$x->[0]);
9b924220 2218 return $t;
990fb837 2219 }
61f5c3f5 2220 my $x1 = _copy($c,$x);
2221
2222 my $es = '';
1ddff52a 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 }
091c87b1 2232 while (!(@$x1 == 1 && $x1->[0] == 0)) # _is_zero()
61f5c3f5 2233 {
2234 ($x1, $xr) = _div($c,$x1,$x10000);
7b29e1e6 2235 $es .= unpack($b,pack('v',$xr->[0]));
61f5c3f5 2236 }
2237 $es = reverse $es;
2238 $es =~ s/^[0]+//; # strip leading zeros
03874afe 2239 '0b' . $es; # return result prepended with 0b
61f5c3f5 2240 }
2241
7b29e1e6 2242sub _as_oct
2243 {
2244 # convert a decimal number to octal (ref to array, return ref to string)
2245 my ($c,$x) = @_;
2246
50109ad0 2247 # fits into one element (handle also 0 case)
7b29e1e6 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
2265sub _from_oct
2266 {
50109ad0 2267 # convert a octal number to decimal (string, return ref to array)
7b29e1e6 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
0617f807 2282 $val = CORE::oct($val);
7b29e1e6 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
394e6ffb 2291sub _from_hex
0716bf9b 2292 {
50109ad0 2293 # convert a hex number to decimal (string, return ref to array)
394e6ffb 2294 my ($c,$hs) = @_;
0716bf9b 2295
2d2b2744 2296 my $m = _new($c, 0x10000000); # 28 bit at a time (<32 bit!)
03874afe 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
394e6ffb 2305 my $mul = _one();
394e6ffb 2306 my $x = _zero();
0716bf9b 2307
03874afe 2308 my $len = int( (length($hs)-2)/$d ); # $d digit parts, w/o the '0x'
2309 my $val; my $i = -$d;
394e6ffb 2310 while ($len >= 0)
2311 {
03874afe 2312 $val = substr($hs,$i,$d); # get hex digits
7b29e1e6 2313 $val =~ s/^0x// if $len == 0; # for last part only because
0617f807 2314 $val = CORE::hex($val); # hex does not like wrong chars
03874afe 2315 $i -= $d; $len --;
2d2b2744 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;
394e6ffb 2324 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul
2325 }
2326 $x;
2327 }
2328
2329sub _from_bin
0716bf9b 2330 {
50109ad0 2331 # convert a hex number to decimal (string, return ref to array)
394e6ffb 2332 my ($c,$bs) = @_;
0716bf9b 2333
091c87b1 2334 # instead of converting X (8) bit at a time, it is faster to "convert" the
13a12e00 2335 # number to hex, and then call _from_hex.
2336
9b924220 2337 my $hs = $bs;
13a12e00 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
03874afe 2341 my $h = '0x' . unpack('H*', pack ('B*', $hs)); # repack as hex
091c87b1 2342
03874afe 2343 $c->_from_hex($h);
0716bf9b 2344 }
2345
07d34614 2346##############################################################################
2347# special modulus functions
2348
56d9de68 2349sub _modinv
d614cd8b 2350 {
56d9de68 2351 # modular inverse
2352 my ($c,$x,$y) = @_;
1ddff52a 2353
56d9de68 2354 my $u = _zero($c); my $u1 = _one($c);
2355 my $a = _copy($c,$y); my $b = _copy($c,$x);
1ddff52a 2356
2357 # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the
56d9de68 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;
1ddff52a 2362 while (!_is_zero($c,$b))
2363 {
56d9de68 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
1ddff52a 2371 }
2372
2373 # if the gcd is not 1, then return NaN
56d9de68 2374 return (undef,undef) unless _is_one($c,$a);
2375
03874afe 2376 ($u1, $sign == 1 ? '+' : '-');
d614cd8b 2377 }
2378
2379sub _modpow
2380 {
2381 # modulus of power ($x ** $y) % $z
07d34614 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 }
1ddff52a 2395
2396# $num = _mod($c,$num,$mod); # this does not make it faster
07d34614 2397
2398 my $acc = _copy($c,$num); my $t = _one();
2399
9b924220 2400 my $expbin = _as_bin($c,$exp); $expbin =~ s/^0b//;
1ddff52a 2401 my $len = length($expbin);
2402 while (--$len >= 0)
07d34614 2403 {
1ddff52a 2404 if ( substr($expbin,$len,1) eq '1') # is_odd
07d34614 2405 {
2406 _mul($c,$t,$acc);
2407 $t = _mod($c,$t,$mod);
2408 }
2409 _mul($c,$acc,$acc);
2410 $acc = _mod($c,$acc,$mod);
07d34614 2411 }
2412 @$num = @$t;
2413 $num;
d614cd8b 2414 }
2415
9b924220 2416sub _gcd
2417 {
2418 # greatest common divisor
2419 my ($c,$x,$y) = @_;
2420
b68b7ab1 2421 while ( (scalar @$y != 1) || ($y->[0] != 0) ) # while ($y != 0)
9b924220 2422 {
2423 my $t = _copy($c,$y);
2424 $y = _mod($c, $x, $y);
2425 $x = $t;
2426 }
2427 $x;
2428 }
2429
394e6ffb 2430##############################################################################
2431##############################################################################
2432
0716bf9b 24331;
2434__END__
2435
2436=head1 NAME
2437
2438Math::BigInt::Calc - Pure Perl module to support Math::BigInt
2439
2440=head1 SYNOPSIS
2441
ee15d750 2442Provides support for big integer calculations. Not intended to be used by other
091c87b1 2443modules. Other modules which sport the same functions can also be used to support
2444Math::BigInt, like Math::BigInt::GMP or Math::BigInt::Pari.
0716bf9b 2445
2446=head1 DESCRIPTION
2447
027dc388 2448In order to allow for multiple big integer libraries, Math::BigInt was
2449rewritten to use library modules for core math routines. Any module which
2450follows the same API as this can be used instead by using the following:
0716bf9b 2451
ee15d750 2452 use Math::BigInt lib => 'libname';
0716bf9b 2453
027dc388 2454'libname' is either the long name ('Math::BigInt::Pari'), or only the short
2455version like 'Pari'.
2456
990fb837 2457=head1 STORAGE
2458
2459=head1 METHODS
0716bf9b 2460
027dc388 2461The following functions MUST be defined in order to support the use by
9b924220 2462Math::BigInt v1.70 or later:
0716bf9b 2463
50109ad0 2464 api_version() return API version, 1 for v1.70, 2 for v1.83
0716bf9b 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
9b924220 2468 _two() return a new object with value 2
2469 _ten() return a new object with value 10
0716bf9b 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
3c4b39be 2475 machine-dependent floating point size limitations
0716bf9b 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
b22b3e31 2480 In list context, returns (result,remainder).
2481 NOTE: this is integer math, so no
2482 fractional part will be returned.
990fb837 2483 The second operand will be not be 0, so no need to
2484 check for that.
b22b3e31 2485 _sub(obj,obj) Simple subtraction of 1 object from another
0716bf9b 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!
3c4b39be 2490 _dec(obj) decrement object by one (input is guaranteed to be > 0)
e745a66c 2491 _inc(obj) increment object by one
2492
0716bf9b 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
9b924220 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
0716bf9b 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
50109ad0 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
0716bf9b 2514
9b924220 2515 _as_hex(str) return string containing the value as
ee15d750 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
0716bf9b 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
dccbb853 2526 Note: XOR, AND and OR pad with zeros if size mismatches
0716bf9b 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
50109ad0 2530 _mod(obj1,obj2) Return remainder of div of the 1st by the 2nd object
990fb837 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)
b3abae2a 2533 _fac(obj) return factorial of object 1 (1*2*3*4..)
50109ad0 2534 _pow(obj1,obj2) return object 1 to the power of object 2
b282a552 2535 return undef for NaN
b22b3e31 2536 _zeros(obj) return number of trailing decimal zeros
d614cd8b 2537 _modinv return inverse modulus
2538 _modpow return modulus of power ($x ** $y) % $z
091c87b1 2539 _log_int(X,N) calculate integer log() of X in base N
2540 X >= 0, N >= 0 (return undef for NaN)
8df1e0a2 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
9b924220 2545 _gcd(obj,obj) return Greatest Common Divisor of two objects
2546
50109ad0 2547The 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
9b924220 2555The following functions are optional, and can be defined if the underlying lib
2556has a fast way to do them. If undefined, Math::BigInt will use pure Perl (hence
2557slow) fallback routines to emulate these:
2558
2559 _signed_or
2560 _signed_and
2561 _signed_xor
2562
b22b3e31 2563Input strings come in as unsigned but with prefix (i.e. as '123', '0xabc'
0716bf9b 2564or '0b1101').
2565
990fb837 2566So the library needs only to deal with unsigned big integers. Testing of input
2567parameter validity is done by the caller, so you need not worry about
2568underflow (f.i. in C<_sub()>, C<_dec()>) nor about division by zero or similar
2569cases.
574bacfe 2570
2571The first parameter can be modified, that includes the possibility that you
2572return a reference to a completely different object instead. Although keeping
50109ad0 2573the reference and just changing its contents is preferred over creating and
dccbb853 2574returning a different reference.
574bacfe 2575
990fb837 2576Return values are always references to objects, strings, or true/false for
3c4b39be 2577comparison routines.
990fb837 2578
574bacfe 2579=head1 WRAP YOUR OWN
2580
2581If you want to port your own favourite c-lib for big numbers to the
2582Math::BigInt interface, you can take any of the already existing modules as
2583a rough guideline. You should really wrap up the latest BigInt and BigFloat
bd05a461 2584testsuites with your module, and replace in them any of the following:
574bacfe 2585
2586 use Math::BigInt;
2587
bd05a461 2588by this:
574bacfe 2589
2590 use Math::BigInt lib => 'yourlib';
2591
2592This way you ensure that your library really works 100% within Math::BigInt.
0716bf9b 2593
2594=head1 LICENSE
2595
2596This program is free software; you may redistribute it and/or modify it under
2597the same terms as Perl itself.
2598
2599=head1 AUTHORS
2600
2601Original math code by Mark Biggar, rewritten by Tels L<http://bloodgate.com/>
990fb837 2602in late 2000.
0716bf9b 2603Seperated from BigInt and shaped API with the help of John Peacock.
b68b7ab1 2604
7b29e1e6 2605Fixed, speed-up, streamlined and enhanced by Tels 2001 - 2007.
0716bf9b 2606
2607=head1 SEE ALSO
2608
50109ad0 2609L<Math::BigInt>, L<Math::BigFloat>,
990fb837 2610L<Math::BigInt::GMP>, L<Math::BigInt::FastCalc> and L<Math::BigInt::Pari>.
0716bf9b 2611
2612=cut