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