lib/blib.t tweak for VMS
[p5sagit/p5-mst-13.2.git] / lib / Math / BigInt / Calc.pm
CommitLineData
0716bf9b 1package Math::BigInt::Calc;
2
3use 5.005;
4use strict;
574bacfe 5# use warnings; # dont use warnings for older Perls
0716bf9b 6
7require Exporter;
bd05a461 8use vars qw/@ISA $VERSION/;
0716bf9b 9@ISA = qw(Exporter);
10
2e507a43 11$VERSION = '0.27';
0716bf9b 12
13# Package to store unsigned big integers in decimal and do math with them
14
15# Internally the numbers are stored in an array with at least 1 element, no
027dc388 16# leading zero parts (except the first) and in base 1eX where X is determined
17# automatically at loading time to be the maximum possible value
0716bf9b 18
19# todo:
20# - fully remove funky $# stuff (maybe)
0716bf9b 21
22# USE_MUL: due to problems on certain os (os390, posix-bc) "* 1e-5" is used
ee15d750 23# instead of "/ 1e5" at some places, (marked with USE_MUL). Other platforms
24# BS2000, some Crays need USE_DIV instead.
bd05a461 25# The BEGIN block is used to determine which of the two variants gives the
26# correct result.
0716bf9b 27
28##############################################################################
29# global constants, flags and accessory
30
31# constants for easier life
32my $nan = 'NaN';
61f5c3f5 33my ($MBASE,$BASE,$RBASE,$BASE_LEN,$MAX_VAL,$BASE_LEN2,$BASE_LEN_SMALL);
394e6ffb 34my ($AND_BITS,$XOR_BITS,$OR_BITS);
35my ($AND_MASK,$XOR_MASK,$OR_MASK);
61f5c3f5 36my ($LEN_CONVERT);
ee15d750 37
38sub _base_len
39 {
dccbb853 40 # set/get the BASE_LEN and assorted other, connected values
41 # used only be the testsuite, set is used only by the BEGIN block below
394e6ffb 42 shift;
43
ee15d750 44 my $b = shift;
45 if (defined $b)
46 {
61f5c3f5 47 # find whether we can use mul or div or none in mul()/div()
48 # (in last case reduce BASE_LEN_SMALL)
49 $BASE_LEN_SMALL = $b+1;
50 my $caught = 0;
51 while (--$BASE_LEN_SMALL > 5)
394e6ffb 52 {
61f5c3f5 53 $MBASE = int("1e".$BASE_LEN_SMALL);
54 $RBASE = abs('1e-'.$BASE_LEN_SMALL); # see USE_MUL
394e6ffb 55 $caught = 0;
61f5c3f5 56 $caught += 1 if (int($MBASE * $RBASE) != 1); # should be 1
57 $caught += 2 if (int($MBASE / $MBASE) != 1); # should be 1
394e6ffb 58 last if $caught != 3;
59 }
61f5c3f5 60 # BASE_LEN is used for anything else than mul()/div()
61 $BASE_LEN = $BASE_LEN_SMALL;
62 $BASE_LEN = shift if (defined $_[0]); # one more arg?
ee15d750 63 $BASE = int("1e".$BASE_LEN);
61f5c3f5 64
65 $BASE_LEN2 = int($BASE_LEN_SMALL / 2); # for mul shortcut
66 $MBASE = int("1e".$BASE_LEN_SMALL);
67 $RBASE = abs('1e-'.$BASE_LEN_SMALL); # see USE_MUL
68 $MAX_VAL = $MBASE-1;
69 $LEN_CONVERT = 0;
70 $LEN_CONVERT = 1 if $BASE_LEN_SMALL != $BASE_LEN;
71
72 #print "BASE_LEN: $BASE_LEN MAX_VAL: $MAX_VAL BASE: $BASE RBASE: $RBASE ";
73 #print "BASE_LEN_SMALL: $BASE_LEN_SMALL MBASE: $MBASE\n";
74
394e6ffb 75 if ($caught & 1 != 0)
ee15d750 76 {
77 # must USE_MUL
ee15d750 78 *{_mul} = \&_mul_use_mul;
79 *{_div} = \&_div_use_mul;
80 }
394e6ffb 81 else # $caught must be 2, since it can't be 1 nor 3
ee15d750 82 {
ee15d750 83 # can USE_DIV instead
84 *{_mul} = \&_mul_use_div;
85 *{_div} = \&_div_use_div;
86 }
87 }
61f5c3f5 88 return $BASE_LEN unless wantarray;
89 return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN_SMALL, $MAX_VAL);
ee15d750 90 }
574bacfe 91
92BEGIN
93 {
bd05a461 94 # from Daniel Pfeiffer: determine largest group of digits that is precisely
574bacfe 95 # multipliable with itself plus carry
dccbb853 96 # Test now changed to expect the proper pattern, not a result off by 1 or 2
97 my ($e, $num) = 3; # lowest value we will use is 3+1-1 = 3
bd05a461 98 do
99 {
100 $num = ('9' x ++$e) + 0;
394e6ffb 101 $num *= $num + 1.0;
394e6ffb 102 } while ("$num" =~ /9{$e}0{$e}/); # must be a certain pattern
103 $e--; # last test failed, so retract one step
104 # the limits below brush the problems with the test above under the rug:
105 # the test should be able to find the proper $e automatically
106 $e = 5 if $^O =~ /^uts/; # UTS get's some special treatment
107 $e = 5 if $^O =~ /^unicos/; # unicos is also problematic (6 seems to work
108 # there, but we play safe)
2e507a43 109 $e = 7 if $e > 7; # cap, for VMS, OS/390 and other 64 bit systems
110 # 8 fails inside random testsuite, so take 7
394e6ffb 111
61f5c3f5 112 # determine how many digits fit into an integer and can be safely added
113 # together plus carry w/o causing an overflow
114
115 # this below detects 15 on a 64 bit system, because after that it becomes
116 # 1e16 and not 1000000 :/ I can make it detect 18, but then I get a lot of
117 # test failures. Ugh! (Tomake detect 18: uncomment lines marked with *)
118 use integer;
119 my $bi = 5; # approx. 16 bit
120 $num = int('9' x $bi);
121 # $num = 99999; # *
122 # while ( ($num+$num+1) eq '1' . '9' x $bi) # *
123 while ( int($num+$num+1) eq '1' . '9' x $bi)
124 {
125 $bi++; $num = int('9' x $bi);
126 # $bi++; $num *= 10; $num += 9; # *
127 }
128 $bi--; # back off one step
129 # by setting them equal, we ignore the findings and use the default
130 # one-size-fits-all approach from former versions
131 $bi = $e; # XXX, this should work always
132
133 __PACKAGE__->_base_len($e,$bi); # set and store
394e6ffb 134
135 # find out how many bits _and, _or and _xor can take (old default = 16)
136 # I don't think anybody has yet 128 bit scalars, so let's play safe.
394e6ffb 137 local $^W = 0; # don't warn about 'nonportable number'
138 $AND_BITS = 15; $XOR_BITS = 15; $OR_BITS = 15;
139
140 # find max bits, we will not go higher than numberofbits that fit into $BASE
141 # to make _and etc simpler (and faster for smaller, slower for large numbers)
142 my $max = 16;
143 while (2 ** $max < $BASE) { $max++; }
144 my ($x,$y,$z);
145 do {
146 $AND_BITS++;
147 $x = oct('0b' . '1' x $AND_BITS); $y = $x & $x;
148 $z = (2 ** $AND_BITS) - 1;
149 } while ($AND_BITS < $max && $x == $z && $y == $x);
150 $AND_BITS --; # retreat one step
151 do {
152 $XOR_BITS++;
153 $x = oct('0b' . '1' x $XOR_BITS); $y = $x ^ 0;
154 $z = (2 ** $XOR_BITS) - 1;
155 } while ($XOR_BITS < $max && $x == $z && $y == $x);
156 $XOR_BITS --; # retreat one step
157 do {
158 $OR_BITS++;
159 $x = oct('0b' . '1' x $OR_BITS); $y = $x | $x;
160 $z = (2 ** $OR_BITS) - 1;
161 } while ($OR_BITS < $max && $x == $z && $y == $x);
162 $OR_BITS --; # retreat one step
163
574bacfe 164 }
165
0716bf9b 166##############################################################################
61f5c3f5 167# convert between the "small" and the "large" representation
168
169sub _to_large
170 {
171 # take an array in base $BASE_LEN_SMALL and convert it in-place to $BASE_LEN
172 my ($c,$x) = @_;
173
174# print "_to_large $BASE_LEN_SMALL => $BASE_LEN\n";
175
176 return $x if $LEN_CONVERT == 0 || # nothing to converconvertor
177 @$x == 1; # only one element => early out
178
179 # 12345 67890 12345 67890 contents
180 # to 3 2 1 0 index
181 # 123456 7890123 4567890 contents
182
183# # faster variant
184# my @d; my $str = '';
185# my $z = '0' x $BASE_LEN_SMALL;
186# foreach (@$x)
187# {
188# # ... . 04321 . 000321
189# $str = substr($z.$_,-$BASE_LEN_SMALL,$BASE_LEN_SMALL) . $str;
190# if (length($str) > $BASE_LEN)
191# {
192# push @d, substr($str,-$BASE_LEN,$BASE_LEN); # extract one piece
193# substr($str,-$BASE_LEN,$BASE_LEN) = ''; # remove it
194# }
195# }
196# push @d, $str if $str !~ /^0*$/; # extract last piece
197# @$x = @d;
198# $x->[-1] = int($x->[-1]); # strip leading zero
199# $x;
200
201 my $ret = "";
202 my $l = scalar @$x; # number of parts
203 $l --; $ret .= int($x->[$l]); $l--;
204 my $z = '0' x ($BASE_LEN_SMALL-1);
205 while ($l >= 0)
206 {
207 $ret .= substr($z.$x->[$l],-$BASE_LEN_SMALL);
208 $l--;
209 }
210 my $str = _new($c,\$ret); # make array
211 @$x = @$str; # clobber contents of $x
212 $x->[-1] = int($x->[-1]); # strip leading zero
213 }
214
215sub _to_small
216 {
217 # take an array in base $BASE_LEN and convert it in-place to $BASE_LEN_SMALL
218 my ($c,$x) = @_;
219
220 return $x if $LEN_CONVERT == 0; # nothing to do
221 return $x if @$x == 1 && length(int($x->[0])) <= $BASE_LEN_SMALL;
222
223 my $d = _str($c,$x);
224 my $il = length($$d)-1;
225 ## this leaves '00000' instead of int 0 and will be corrected after any op
226 # clobber contents of $x
227 @$x = reverse(unpack("a" . ($il % $BASE_LEN_SMALL+1)
228 . ("a$BASE_LEN_SMALL" x ($il / $BASE_LEN_SMALL)), $$d));
229
230 $x->[-1] = int($x->[-1]); # strip leading zero
231 }
232
233###############################################################################
0716bf9b 234
235sub _new
236 {
394e6ffb 237 # (ref to string) return ref to num_array
9393ace2 238 # Convert a number from string format (without sign) to internal base
239 # 1ex format. Assumes normalized value as input.
574bacfe 240 my $d = $_[1];
61f5c3f5 241 my $il = length($$d)-1;
242 # this leaves '00000' instead of int 0 and will be corrected after any op
243 [ reverse(unpack("a" . ($il % $BASE_LEN+1)
574bacfe 244 . ("a$BASE_LEN" x ($il / $BASE_LEN)), $$d)) ];
0716bf9b 245 }
394e6ffb 246
247BEGIN
248 {
249 $AND_MASK = __PACKAGE__->_new( \( 2 ** $AND_BITS ));
250 $XOR_MASK = __PACKAGE__->_new( \( 2 ** $XOR_BITS ));
251 $OR_MASK = __PACKAGE__->_new( \( 2 ** $OR_BITS ));
252 }
0716bf9b 253
254sub _zero
255 {
256 # create a zero
61f5c3f5 257 [ 0 ];
0716bf9b 258 }
259
260sub _one
261 {
262 # create a one
61f5c3f5 263 [ 1 ];
0716bf9b 264 }
265
027dc388 266sub _two
267 {
268 # create a two (for _pow)
61f5c3f5 269 [ 2 ];
027dc388 270 }
271
0716bf9b 272sub _copy
273 {
61f5c3f5 274 [ @{$_[1]} ];
0716bf9b 275 }
276
bd05a461 277# catch and throw away
278sub import { }
279
0716bf9b 280##############################################################################
281# convert back to string and number
282
283sub _str
284 {
285 # (ref to BINT) return num_str
286 # Convert number from internal base 100000 format to string format.
287 # internal format is always normalized (no leading zeros, "-0" => "+0")
574bacfe 288 my $ar = $_[1];
0716bf9b 289 my $ret = "";
61f5c3f5 290
291 my $l = scalar @$ar; # number of parts
292 return $nan if $l < 1; # should not happen
293
0716bf9b 294 # handle first one different to strip leading zeros from it (there are no
295 # leading zero parts in internal representation)
61f5c3f5 296 $l --; $ret .= int($ar->[$l]); $l--;
0716bf9b 297 # Interestingly, the pre-padd method uses more time
574bacfe 298 # the old grep variant takes longer (14 to 10 sec)
299 my $z = '0' x ($BASE_LEN-1);
0716bf9b 300 while ($l >= 0)
301 {
574bacfe 302 $ret .= substr($z.$ar->[$l],-$BASE_LEN); # fastest way I could think of
0716bf9b 303 $l--;
304 }
61f5c3f5 305 \$ret;
0716bf9b 306 }
307
308sub _num
309 {
310 # Make a number (scalar int/float) from a BigInt object
574bacfe 311 my $x = $_[1];
0716bf9b 312 return $x->[0] if scalar @$x == 1; # below $BASE
313 my $fac = 1;
314 my $num = 0;
315 foreach (@$x)
316 {
317 $num += $fac*$_; $fac *= $BASE;
318 }
61f5c3f5 319 $num;
0716bf9b 320 }
321
322##############################################################################
323# actual math code
324
325sub _add
326 {
327 # (ref to int_num_array, ref to int_num_array)
574bacfe 328 # routine to add two base 1eX numbers
0716bf9b 329 # stolen from Knuth Vol 2 Algorithm A pg 231
b22b3e31 330 # there are separate routines to add and sub as per Knuth pg 233
0716bf9b 331 # This routine clobbers up array x, but not y.
332
574bacfe 333 my ($c,$x,$y) = @_;
b3abae2a 334
335 return $x if (@$y == 1) && $y->[0] == 0; # $x + 0 => $x
336 if ((@$x == 1) && $x->[0] == 0) # 0 + $y => $y->copy
337 {
338 # twice as slow as $x = [ @$y ], but necc. to retain $x as ref :(
339 @$x = @$y; return $x;
340 }
0716bf9b 341
342 # for each in Y, add Y to X and carry. If after that, something is left in
343 # X, foreach in X add carry to X and then return X, carry
344 # Trades one "$j++" for having to shift arrays, $j could be made integer
b22b3e31 345 # but this would impose a limit to number-length of 2**32.
0716bf9b 346 my $i; my $car = 0; my $j = 0;
347 for $i (@$y)
348 {
e745a66c 349 $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0;
0716bf9b 350 $j++;
351 }
352 while ($car != 0)
353 {
354 $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0; $j++;
355 }
61f5c3f5 356 $x;
e745a66c 357 }
358
359sub _inc
360 {
361 # (ref to int_num_array, ref to int_num_array)
362 # routine to add 1 to a base 1eX numbers
363 # This routine clobbers up array x, but not y.
364 my ($c,$x) = @_;
365
366 for my $i (@$x)
367 {
368 return $x if (($i += 1) < $BASE); # early out
61f5c3f5 369 $i = 0; # overflow, next
e745a66c 370 }
61f5c3f5 371 push @$x,1 if ($x->[-1] == 0); # last overflowed, so extend
372 $x;
e745a66c 373 }
374
375sub _dec
376 {
377 # (ref to int_num_array, ref to int_num_array)
378 # routine to add 1 to a base 1eX numbers
379 # This routine clobbers up array x, but not y.
380 my ($c,$x) = @_;
381
61f5c3f5 382 my $MAX = $BASE-1; # since MAX_VAL based on MBASE
e745a66c 383 for my $i (@$x)
384 {
385 last if (($i -= 1) >= 0); # early out
61f5c3f5 386 $i = $MAX; # overflow, next
e745a66c 387 }
388 pop @$x if $x->[-1] == 0 && @$x > 1; # last overflowed (but leave 0)
61f5c3f5 389 $x;
0716bf9b 390 }
391
392sub _sub
393 {
9393ace2 394 # (ref to int_num_array, ref to int_num_array, swap)
574bacfe 395 # subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
56b9c951 396 # subtract Y from X by modifying x in place
574bacfe 397 my ($c,$sx,$sy,$s) = @_;
0716bf9b 398
399 my $car = 0; my $i; my $j = 0;
400 if (!$s)
401 {
402 #print "case 2\n";
403 for $i (@$sx)
404 {
405 last unless defined $sy->[$j] || $car;
0716bf9b 406 $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0); $j++;
0716bf9b 407 }
408 # might leave leading zeros, so fix that
394e6ffb 409 return __strip_zeros($sx);
0716bf9b 410 }
394e6ffb 411 #print "case 1 (swap)\n";
412 for $i (@$sx)
0716bf9b 413 {
56b9c951 414 # we can't do an early out if $x is than $y, since we
415 # need to copy the high chunks from $y. Found by Bob Mathews.
416 #last unless defined $sy->[$j] || $car;
394e6ffb 417 $sy->[$j] += $BASE
418 if $car = (($sy->[$j] = $i-($sy->[$j]||0) - $car) < 0);
419 $j++;
0716bf9b 420 }
394e6ffb 421 # might leave leading zeros, so fix that
422 __strip_zeros($sy);
0716bf9b 423 }
424
9393ace2 425sub _square_use_mul
426 {
427 # compute $x ** 2 or $x * $x in-place and return $x
428 my ($c,$x) = @_;
429
430 # From: Handbook of Applied Cryptography by A. Menezes, P. van Oorschot and
431 # S. Vanstone., Chapter 14
432
433 #14.16 Algorithm Multiple-precision squaring
434 #INPUT: positive integer x = (xt 1 xt 2 ... x1 x0)b.
435 #OUTPUT: x * x = x ** 2 in radix b representation.
436 #1. For i from 0 to (2t - 1) do: wi <- 0.
437 #2. For i from 0 to (t - 1) do the following:
438 # 2.1 (uv)b w2i + xi * xi, w2i v, c u.
439 # 2.2 For j from (i + 1)to (t - 1) do the following:
440 # (uv)b <- wi+j + 2*xj * xi + c, wi+j <- v, c <- u.
441 # 2.3 wi+t <- u.
442 #3. Return((w2t-1 w2t-2 ... w1 w0)b).
443
444# # Note: That description is crap. Half of the symbols are not explained or
445# # used with out beeing set.
446# my $t = scalar @$x; # count
447# my ($c,$i,$j);
448# for ($i = 0; $i < $t; $i++)
449# {
450# $x->[$i] = $x->[$i*2] + $x[$i]*$x[$i];
451# $x->[$i*2] = $x[$i]; $c = $x[$i];
452# for ($j = $i+1; $j < $t; $j++)
453# {
454# $x->[$i] = $x->[$i+$j] + 2 * $x->[$i] * $x->[$j];
455# $x->[$i+$j] = $x[$j]; $c = $x[$i];
456# }
457# $x->[$i+$t] = $x[$i];
458# }
459 $x;
460 }
461
ee15d750 462sub _mul_use_mul
0716bf9b 463 {
9393ace2 464 # (ref to int_num_array, ref to int_num_array)
0716bf9b 465 # multiply two numbers in internal representation
b22b3e31 466 # modifies first arg, second need not be different from first
574bacfe 467 my ($c,$xv,$yv) = @_;
dccbb853 468
b3abae2a 469 # shortcut for two very short numbers (improved by Nathan Zook)
61f5c3f5 470 # works also if xv and yv are the same reference
b3abae2a 471 if ((@$xv == 1) && (@$yv == 1))
472 {
473 if (($xv->[0] *= $yv->[0]) >= $MBASE)
474 {
475 $xv->[0] = $xv->[0] - ($xv->[1] = int($xv->[0] * $RBASE)) * $MBASE;
476 };
477 return $xv;
478 }
479 # shortcut for result == 0
480 if ( ((@$xv == 1) && ($xv->[0] == 0)) ||
481 ((@$yv == 1) && ($yv->[0] == 0)) )
482 {
483 @$xv = (0);
484 return $xv;
485 }
486
0716bf9b 487 # since multiplying $x with $x fails, make copy in this case
574bacfe 488 $yv = [@$xv] if "$xv" eq "$yv"; # same references?
9393ace2 489 # since multiplying $x with $x would fail here, use the faster squaring
490# return _square($c,$xv) if "$xv" eq "$yv"; # same reference?
491
61f5c3f5 492 if ($LEN_CONVERT != 0)
493 {
494 $c->_to_small($xv); $c->_to_small($yv);
495 }
496
497 my @prod = (); my ($prod,$car,$cty,$xi,$yi);
498
0716bf9b 499 for $xi (@$xv)
500 {
501 $car = 0; $cty = 0;
574bacfe 502
503 # slow variant
504# for $yi (@$yv)
505# {
506# $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
507# $prod[$cty++] =
61f5c3f5 508# $prod - ($car = int($prod * RBASE)) * $MBASE; # see USE_MUL
574bacfe 509# }
510# $prod[$cty] += $car if $car; # need really to check for 0?
511# $xi = shift @prod;
512
513 # faster variant
514 # looping through this if $xi == 0 is silly - so optimize it away!
515 $xi = (shift @prod || 0), next if $xi == 0;
0716bf9b 516 for $yi (@$yv)
517 {
518 $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
574bacfe 519## this is actually a tad slower
520## $prod = $prod[$cty]; $prod += ($car + $xi * $yi); # no ||0 here
0716bf9b 521 $prod[$cty++] =
61f5c3f5 522 $prod - ($car = int($prod * $RBASE)) * $MBASE; # see USE_MUL
0716bf9b 523 }
524 $prod[$cty] += $car if $car; # need really to check for 0?
027dc388 525 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy
0716bf9b 526 }
0716bf9b 527 push @$xv, @prod;
61f5c3f5 528 if ($LEN_CONVERT != 0)
529 {
530 $c->_to_large($yv);
531 $c->_to_large($xv);
532 }
533 else
534 {
535 __strip_zeros($xv);
536 }
537 $xv;
0716bf9b 538 }
539
ee15d750 540sub _mul_use_div
541 {
9393ace2 542 # (ref to int_num_array, ref to int_num_array)
ee15d750 543 # multiply two numbers in internal representation
544 # modifies first arg, second need not be different from first
545 my ($c,$xv,$yv) = @_;
546
b3abae2a 547 # shortcut for two very short numbers (improved by Nathan Zook)
61f5c3f5 548 # works also if xv and yv are the same reference
b3abae2a 549 if ((@$xv == 1) && (@$yv == 1))
550 {
551 if (($xv->[0] *= $yv->[0]) >= $MBASE)
552 {
553 $xv->[0] =
554 $xv->[0] - ($xv->[1] = int($xv->[0] / $MBASE)) * $MBASE;
555 };
556 return $xv;
557 }
558 # shortcut for result == 0
559 if ( ((@$xv == 1) && ($xv->[0] == 0)) ||
560 ((@$yv == 1) && ($yv->[0] == 0)) )
561 {
562 @$xv = (0);
563 return $xv;
564 }
565
61f5c3f5 566
ee15d750 567 # since multiplying $x with $x fails, make copy in this case
568 $yv = [@$xv] if "$xv" eq "$yv"; # same references?
9393ace2 569 # since multiplying $x with $x would fail here, use the faster squaring
570# return _square($c,$xv) if "$xv" eq "$yv"; # same reference?
571
61f5c3f5 572 if ($LEN_CONVERT != 0)
573 {
574 $c->_to_small($xv); $c->_to_small($yv);
575 }
576
577 my @prod = (); my ($prod,$car,$cty,$xi,$yi);
ee15d750 578 for $xi (@$xv)
579 {
580 $car = 0; $cty = 0;
581 # looping through this if $xi == 0 is silly - so optimize it away!
582 $xi = (shift @prod || 0), next if $xi == 0;
583 for $yi (@$yv)
584 {
585 $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
586 $prod[$cty++] =
61f5c3f5 587 $prod - ($car = int($prod / $MBASE)) * $MBASE;
ee15d750 588 }
589 $prod[$cty] += $car if $car; # need really to check for 0?
027dc388 590 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy
ee15d750 591 }
592 push @$xv, @prod;
61f5c3f5 593 if ($LEN_CONVERT != 0)
594 {
595 $c->_to_large($yv);
596 $c->_to_large($xv);
597 }
598 else
599 {
600 __strip_zeros($xv);
601 }
602 $xv;
ee15d750 603 }
604
605sub _div_use_mul
0716bf9b 606 {
b22b3e31 607 # ref to array, ref to array, modify first array and return remainder if
0716bf9b 608 # in list context
574bacfe 609 my ($c,$x,$yorg) = @_;
0716bf9b 610
61f5c3f5 611 if (@$x == 1 && @$yorg == 1)
612 {
13a12e00 613 # shortcut, $yorg and $x are two small numbers
61f5c3f5 614 if (wantarray)
615 {
616 my $r = [ $x->[0] % $yorg->[0] ];
617 $x->[0] = int($x->[0] / $yorg->[0]);
618 return ($x,$r);
619 }
620 else
621 {
622 $x->[0] = int($x->[0] / $yorg->[0]);
623 return $x;
624 }
625 }
28df3e88 626 if (@$yorg == 1)
627 {
628 my $rem;
629 $rem = _mod($c,[ @$x ],$yorg) if wantarray;
13a12e00 630
28df3e88 631 # shortcut, $y is < $BASE
632 my $j = scalar @$x; my $r = 0;
633 my $y = $yorg->[0]; my $b;
634 while ($j-- > 0)
635 {
636 $b = $r * $MBASE + $x->[$j];
637 $x->[$j] = int($b/$y);
638 $r = $b % $y;
639 }
640 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero
641 return ($x,$rem) if wantarray;
642 return $x;
643 }
0716bf9b 644
0716bf9b 645 my $y = [ @$yorg ];
61f5c3f5 646 if ($LEN_CONVERT != 0)
647 {
648 $c->_to_small($x); $c->_to_small($y);
649 }
650
651 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);
652
653 $car = $bar = $prd = 0;
654 if (($dd = int($MBASE/($y->[-1]+1))) != 1)
0716bf9b 655 {
656 for $xi (@$x)
657 {
658 $xi = $xi * $dd + $car;
61f5c3f5 659 $xi -= ($car = int($xi * $RBASE)) * $MBASE; # see USE_MUL
0716bf9b 660 }
661 push(@$x, $car); $car = 0;
662 for $yi (@$y)
663 {
664 $yi = $yi * $dd + $car;
61f5c3f5 665 $yi -= ($car = int($yi * $RBASE)) * $MBASE; # see USE_MUL
0716bf9b 666 }
667 }
668 else
669 {
670 push(@$x, 0);
671 }
672 @q = (); ($v2,$v1) = @$y[-2,-1];
673 $v2 = 0 unless $v2;
674 while ($#$x > $#$y)
675 {
676 ($u2,$u1,$u0) = @$x[-3..-1];
677 $u2 = 0 unless $u2;
678 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
679 # if $v1 == 0;
61f5c3f5 680 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1));
681 --$q while ($v2*$q > ($u0*$MBASE+$u1-$q*$v1)*$MBASE+$u2);
0716bf9b 682 if ($q)
683 {
684 ($car, $bar) = (0,0);
685 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
686 {
687 $prd = $q * $y->[$yi] + $car;
61f5c3f5 688 $prd -= ($car = int($prd * $RBASE)) * $MBASE; # see USE_MUL
689 $x->[$xi] += $MBASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0));
0716bf9b 690 }
691 if ($x->[-1] < $car + $bar)
692 {
693 $car = 0; --$q;
694 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
695 {
61f5c3f5 696 $x->[$xi] -= $MBASE
697 if ($car = (($x->[$xi] += $y->[$yi] + $car) > $MBASE));
0716bf9b 698 }
699 }
700 }
701 pop(@$x); unshift(@q, $q);
702 }
703 if (wantarray)
704 {
705 @d = ();
706 if ($dd != 1)
707 {
708 $car = 0;
709 for $xi (reverse @$x)
710 {
61f5c3f5 711 $prd = $car * $MBASE + $xi;
0716bf9b 712 $car = $prd - ($tmp = int($prd / $dd)) * $dd; # see USE_MUL
713 unshift(@d, $tmp);
714 }
715 }
716 else
717 {
718 @d = @$x;
719 }
720 @$x = @q;
61f5c3f5 721 my $d = \@d;
722 if ($LEN_CONVERT != 0)
723 {
724 $c->_to_large($x); $c->_to_large($d);
725 }
726 else
727 {
728 __strip_zeros($x);
729 __strip_zeros($d);
730 }
731 return ($x,$d);
0716bf9b 732 }
733 @$x = @q;
61f5c3f5 734 if ($LEN_CONVERT != 0)
735 {
736 $c->_to_large($x);
737 }
738 else
739 {
740 __strip_zeros($x);
741 }
742 $x;
0716bf9b 743 }
744
ee15d750 745sub _div_use_div
746 {
747 # ref to array, ref to array, modify first array and return remainder if
748 # in list context
ee15d750 749 my ($c,$x,$yorg) = @_;
ee15d750 750
61f5c3f5 751 if (@$x == 1 && @$yorg == 1)
752 {
13a12e00 753 # shortcut, $yorg and $x are two small numbers
61f5c3f5 754 if (wantarray)
755 {
756 my $r = [ $x->[0] % $yorg->[0] ];
757 $x->[0] = int($x->[0] / $yorg->[0]);
758 return ($x,$r);
759 }
760 else
761 {
762 $x->[0] = int($x->[0] / $yorg->[0]);
763 return $x;
764 }
765 }
28df3e88 766 if (@$yorg == 1)
767 {
768 my $rem;
769 $rem = _mod($c,[ @$x ],$yorg) if wantarray;
770
771 # shortcut, $y is < $BASE
772 my $j = scalar @$x; my $r = 0;
773 my $y = $yorg->[0]; my $b;
774 while ($j-- > 0)
775 {
776 $b = $r * $MBASE + $x->[$j];
777 $x->[$j] = int($b/$y);
778 $r = $b % $y;
779 }
780 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero
781 return ($x,$rem) if wantarray;
782 return $x;
783 }
ee15d750 784
ee15d750 785 my $y = [ @$yorg ];
61f5c3f5 786 if ($LEN_CONVERT != 0)
787 {
788 $c->_to_small($x); $c->_to_small($y);
789 }
790
791 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);
792
793 $car = $bar = $prd = 0;
794 if (($dd = int($MBASE/($y->[-1]+1))) != 1)
ee15d750 795 {
796 for $xi (@$x)
797 {
798 $xi = $xi * $dd + $car;
61f5c3f5 799 $xi -= ($car = int($xi / $MBASE)) * $MBASE;
ee15d750 800 }
801 push(@$x, $car); $car = 0;
802 for $yi (@$y)
803 {
804 $yi = $yi * $dd + $car;
61f5c3f5 805 $yi -= ($car = int($yi / $MBASE)) * $MBASE;
ee15d750 806 }
807 }
808 else
809 {
810 push(@$x, 0);
811 }
812 @q = (); ($v2,$v1) = @$y[-2,-1];
813 $v2 = 0 unless $v2;
814 while ($#$x > $#$y)
815 {
816 ($u2,$u1,$u0) = @$x[-3..-1];
817 $u2 = 0 unless $u2;
818 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
819 # if $v1 == 0;
61f5c3f5 820 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1));
821 --$q while ($v2*$q > ($u0*$MBASE+$u1-$q*$v1)*$MBASE+$u2);
ee15d750 822 if ($q)
823 {
824 ($car, $bar) = (0,0);
825 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
826 {
827 $prd = $q * $y->[$yi] + $car;
61f5c3f5 828 $prd -= ($car = int($prd / $MBASE)) * $MBASE;
829 $x->[$xi] += $MBASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0));
ee15d750 830 }
831 if ($x->[-1] < $car + $bar)
832 {
833 $car = 0; --$q;
834 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
835 {
61f5c3f5 836 $x->[$xi] -= $MBASE
837 if ($car = (($x->[$xi] += $y->[$yi] + $car) > $MBASE));
ee15d750 838 }
839 }
840 }
61f5c3f5 841 pop(@$x); unshift(@q, $q);
ee15d750 842 }
843 if (wantarray)
844 {
845 @d = ();
846 if ($dd != 1)
847 {
848 $car = 0;
849 for $xi (reverse @$x)
850 {
61f5c3f5 851 $prd = $car * $MBASE + $xi;
ee15d750 852 $car = $prd - ($tmp = int($prd / $dd)) * $dd;
853 unshift(@d, $tmp);
854 }
855 }
856 else
857 {
858 @d = @$x;
859 }
860 @$x = @q;
61f5c3f5 861 my $d = \@d;
862 if ($LEN_CONVERT != 0)
863 {
864 $c->_to_large($x); $c->_to_large($d);
865 }
866 else
867 {
868 __strip_zeros($x);
869 __strip_zeros($d);
870 }
871 return ($x,$d);
ee15d750 872 }
873 @$x = @q;
61f5c3f5 874 if ($LEN_CONVERT != 0)
875 {
876 $c->_to_large($x);
877 }
878 else
879 {
880 __strip_zeros($x);
881 }
882 $x;
ee15d750 883 }
884
394e6ffb 885##############################################################################
886# testing
887
888sub _acmp
889 {
890 # internal absolute post-normalized compare (ignore signs)
891 # ref to array, ref to array, return <0, 0, >0
892 # arrays must have at least one entry; this is not checked for
893
894 my ($c,$cx,$cy) = @_;
895
61f5c3f5 896 # fast comp based on array elements
394e6ffb 897 my $lxy = scalar @$cx - scalar @$cy;
898 return -1 if $lxy < 0; # already differs, ret
899 return 1 if $lxy > 0; # ditto
900
901 # now calculate length based on digits, not parts
902 $lxy = _len($c,$cx) - _len($c,$cy); # difference
903 return -1 if $lxy < 0;
904 return 1 if $lxy > 0;
905
906 # hm, same lengths, but same contents?
907 my $i = 0; my $a;
908 # first way takes 5.49 sec instead of 4.87, but has the early out advantage
909 # so grep is slightly faster, but more inflexible. hm. $_ instead of $k
910 # yields 5.6 instead of 5.5 sec huh?
911 # manual way (abort if unequal, good for early ne)
912 my $j = scalar @$cx - 1;
913 while ($j >= 0)
9393ace2 914 {
915 last if ($a = $cx->[$j] - $cy->[$j]); $j--;
916 }
917# my $j = scalar @$cx;
918# while (--$j >= 0)
919# {
920# last if ($a = $cx->[$j] - $cy->[$j]);
921# }
394e6ffb 922 return 1 if $a > 0;
923 return -1 if $a < 0;
61f5c3f5 924 0; # equal
925
394e6ffb 926 # while it early aborts, it is even slower than the manual variant
927 #grep { return $a if ($a = $_ - $cy->[$i++]); } @$cx;
928 # grep way, go trough all (bad for early ne)
929 #grep { $a = $_ - $cy->[$i++]; } @$cx;
930 #return $a;
931 }
932
933sub _len
934 {
935 # compute number of digits in bigint, minus the sign
936
937 # int() because add/sub sometimes leaves strings (like '00005') instead of
938 # '5' in this place, thus causing length() to report wrong length
939 my $cx = $_[1];
940
941 return (@$cx-1)*$BASE_LEN+length(int($cx->[-1]));
942 }
943
944sub _digit
945 {
946 # return the nth digit, negative values count backward
947 # zero is rightmost, so _digit(123,0) will give 3
948 my ($c,$x,$n) = @_;
949
950 my $len = _len('',$x);
951
952 $n = $len+$n if $n < 0; # -1 last, -2 second-to-last
953 $n = abs($n); # if negative was too big
954 $len--; $n = $len if $n > $len; # n to big?
955
956 my $elem = int($n / $BASE_LEN); # which array element
957 my $digit = $n % $BASE_LEN; # which digit in this element
958 $elem = '0000'.@$x[$elem]; # get element padded with 0's
959 return substr($elem,-$digit-1,1);
960 }
961
962sub _zeros
963 {
964 # return amount of trailing zeros in decimal
965 # check each array elem in _m for having 0 at end as long as elem == 0
966 # Upon finding a elem != 0, stop
967 my $x = $_[1];
968 my $zeros = 0; my $elem;
969 foreach my $e (@$x)
970 {
971 if ($e != 0)
972 {
973 $elem = "$e"; # preserve x
974 $elem =~ s/.*?(0*$)/$1/; # strip anything not zero
975 $zeros *= $BASE_LEN; # elems * 5
61f5c3f5 976 $zeros += length($elem); # count trailing zeros
394e6ffb 977 last; # early out
978 }
979 $zeros ++; # real else branch: 50% slower!
980 }
61f5c3f5 981 $zeros;
394e6ffb 982 }
983
984##############################################################################
985# _is_* routines
986
987sub _is_zero
988 {
989 # return true if arg (BINT or num_str) is zero (array '+', '0')
990 my $x = $_[1];
61f5c3f5 991
992 (((scalar @$x == 1) && ($x->[0] == 0))) <=> 0;
394e6ffb 993 }
994
995sub _is_even
996 {
997 # return true if arg (BINT or num_str) is even
998 my $x = $_[1];
61f5c3f5 999 (!($x->[0] & 1)) <=> 0;
394e6ffb 1000 }
1001
1002sub _is_odd
1003 {
1004 # return true if arg (BINT or num_str) is even
1005 my $x = $_[1];
61f5c3f5 1006
1007 (($x->[0] & 1)) <=> 0;
394e6ffb 1008 }
1009
1010sub _is_one
1011 {
1012 # return true if arg (BINT or num_str) is one (array '+', '1')
1013 my $x = $_[1];
61f5c3f5 1014
1015 (scalar @$x == 1) && ($x->[0] == 1) <=> 0;
394e6ffb 1016 }
1017
1018sub __strip_zeros
1019 {
1020 # internal normalization function that strips leading zeros from the array
1021 # args: ref to array
1022 my $s = shift;
1023
1024 my $cnt = scalar @$s; # get count of parts
1025 my $i = $cnt-1;
1026 push @$s,0 if $i < 0; # div might return empty results, so fix it
1027
61f5c3f5 1028 return $s if @$s == 1; # early out
1029
394e6ffb 1030 #print "strip: cnt $cnt i $i\n";
1031 # '0', '3', '4', '0', '0',
1032 # 0 1 2 3 4
1033 # cnt = 5, i = 4
1034 # i = 4
1035 # i = 3
1036 # => fcnt = cnt - i (5-2 => 3, cnt => 5-1 = 4, throw away from 4th pos)
1037 # >= 1: skip first part (this can be zero)
1038 while ($i > 0) { last if $s->[$i] != 0; $i--; }
1039 $i++; splice @$s,$i if ($i < $cnt); # $i cant be 0
1040 $s;
1041 }
1042
1043###############################################################################
1044# check routine to test internal state of corruptions
1045
1046sub _check
1047 {
1048 # used by the test suite
1049 my $x = $_[1];
1050
1051 return "$x is not a reference" if !ref($x);
1052
1053 # are all parts are valid?
1054 my $i = 0; my $j = scalar @$x; my ($e,$try);
1055 while ($i < $j)
1056 {
1057 $e = $x->[$i]; $e = 'undef' unless defined $e;
1058 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e)";
1059 last if $e !~ /^[+]?[0-9]+$/;
1060 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (stringify)";
1061 last if "$e" !~ /^[+]?[0-9]+$/;
1062 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (cat-stringify)";
1063 last if '' . "$e" !~ /^[+]?[0-9]+$/;
1064 $try = ' < 0 || >= $BASE; '."($x, $e)";
1065 last if $e <0 || $e >= $BASE;
1066 # this test is disabled, since new/bnorm and certain ops (like early out
1067 # in add/sub) are allowed/expected to leave '00000' in some elements
1068 #$try = '=~ /^00+/; '."($x, $e)";
1069 #last if $e =~ /^00+/;
1070 $i++;
1071 }
1072 return "Illegal part '$e' at pos $i (tested: $try)" if $i < $j;
1073 return 0;
1074 }
1075
1076
1077###############################################################################
1078###############################################################################
1079# some optional routines to make BigInt faster
1080
dccbb853 1081sub _mod
1082 {
1083 # if possible, use mod shortcut
1084 my ($c,$x,$yo) = @_;
1085
1086 # slow way since $y to big
1087 if (scalar @$yo > 1)
1088 {
1089 my ($xo,$rem) = _div($c,$x,$yo);
1090 return $rem;
1091 }
1092 my $y = $yo->[0];
027dc388 1093 # both are single element arrays
dccbb853 1094 if (scalar @$x == 1)
1095 {
1096 $x->[0] %= $y;
1097 return $x;
1098 }
1099
61f5c3f5 1100 # @y is single element, but @x has more than one
dccbb853 1101 my $b = $BASE % $y;
1102 if ($b == 0)
1103 {
1104 # when BASE % Y == 0 then (B * BASE) % Y == 0
1105 # (B * BASE) % $y + A % Y => A % Y
1106 # so need to consider only last element: O(1)
1107 $x->[0] %= $y;
1108 }
027dc388 1109 elsif ($b == 1)
1110 {
28df3e88 1111 # else need to go trough all elements: O(N), but loop is a bit simplified
027dc388 1112 my $r = 0;
1113 foreach (@$x)
1114 {
28df3e88 1115 $r = ($r + $_) % $y; # not much faster, but heh...
1116 #$r += $_ % $y; $r %= $y;
027dc388 1117 }
1118 $r = 0 if $r == $y;
1119 $x->[0] = $r;
1120 }
dccbb853 1121 else
1122 {
027dc388 1123 # else need to go trough all elements: O(N)
1124 my $r = 0; my $bm = 1;
1125 foreach (@$x)
1126 {
28df3e88 1127 $r = ($_ * $bm + $r) % $y;
1128 $bm = ($bm * $b) % $y;
1129
1130 #$r += ($_ % $y) * $bm;
1131 #$bm *= $b;
1132 #$bm %= $y;
1133 #$r %= $y;
027dc388 1134 }
1135 $r = 0 if $r == $y;
1136 $x->[0] = $r;
dccbb853 1137 }
1138 splice (@$x,1);
61f5c3f5 1139 $x;
dccbb853 1140 }
1141
0716bf9b 1142##############################################################################
574bacfe 1143# shifts
1144
1145sub _rsft
1146 {
1147 my ($c,$x,$y,$n) = @_;
1148
1149 if ($n != 10)
1150 {
61f5c3f5 1151 $n = _new($c,\$n); return _div($c,$x, _pow($c,$n,$y));
1152 }
1153
1154 # shortcut (faster) for shifting by 10)
1155 # multiples of $BASE_LEN
1156 my $dst = 0; # destination
1157 my $src = _num($c,$y); # as normal int
1158 my $rem = $src % $BASE_LEN; # remainder to shift
1159 $src = int($src / $BASE_LEN); # source
1160 if ($rem == 0)
1161 {
1162 splice (@$x,0,$src); # even faster, 38.4 => 39.3
574bacfe 1163 }
1164 else
1165 {
61f5c3f5 1166 my $len = scalar @$x - $src; # elems to go
1167 my $vd; my $z = '0'x $BASE_LEN;
1168 $x->[scalar @$x] = 0; # avoid || 0 test inside loop
1169 while ($dst < $len)
574bacfe 1170 {
61f5c3f5 1171 $vd = $z.$x->[$src];
1172 $vd = substr($vd,-$BASE_LEN,$BASE_LEN-$rem);
1173 $src++;
1174 $vd = substr($z.$x->[$src],-$rem,$rem) . $vd;
1175 $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN;
1176 $x->[$dst] = int($vd);
1177 $dst++;
574bacfe 1178 }
61f5c3f5 1179 splice (@$x,$dst) if $dst > 0; # kill left-over array elems
56b9c951 1180 pop @$x if $x->[-1] == 0 && @$x > 1; # kill last element if 0
61f5c3f5 1181 } # else rem == 0
574bacfe 1182 $x;
1183 }
1184
1185sub _lsft
1186 {
1187 my ($c,$x,$y,$n) = @_;
1188
1189 if ($n != 10)
1190 {
61f5c3f5 1191 $n = _new($c,\$n); return _mul($c,$x, _pow($c,$n,$y));
574bacfe 1192 }
61f5c3f5 1193
1194 # shortcut (faster) for shifting by 10) since we are in base 10eX
1195 # multiples of $BASE_LEN:
1196 my $src = scalar @$x; # source
1197 my $len = _num($c,$y); # shift-len as normal int
1198 my $rem = $len % $BASE_LEN; # remainder to shift
1199 my $dst = $src + int($len/$BASE_LEN); # destination
1200 my $vd; # further speedup
1201 $x->[$src] = 0; # avoid first ||0 for speed
1202 my $z = '0' x $BASE_LEN;
1203 while ($src >= 0)
574bacfe 1204 {
61f5c3f5 1205 $vd = $x->[$src]; $vd = $z.$vd;
1206 $vd = substr($vd,-$BASE_LEN+$rem,$BASE_LEN-$rem);
1207 $vd .= $src > 0 ? substr($z.$x->[$src-1],-$BASE_LEN,$rem) : '0' x $rem;
1208 $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN;
1209 $x->[$dst] = int($vd);
1210 $dst--; $src--;
574bacfe 1211 }
61f5c3f5 1212 # set lowest parts to 0
1213 while ($dst >= 0) { $x->[$dst--] = 0; }
1214 # fix spurios last zero element
1215 splice @$x,-1 if $x->[-1] == 0;
574bacfe 1216 $x;
1217 }
1218
027dc388 1219sub _pow
1220 {
1221 # power of $x to $y
1222 # ref to array, ref to array, return ref to array
1223 my ($c,$cx,$cy) = @_;
1224
1225 my $pow2 = _one();
1226 my $two = _two();
1227 my $y1 = _copy($c,$cy);
1228 while (!_is_one($c,$y1))
1229 {
1230 _mul($c,$pow2,$cx) if _is_odd($c,$y1);
1231 _div($c,$y1,$two);
1232 _mul($c,$cx,$cx);
1233 }
1234 _mul($c,$cx,$pow2) unless _is_one($c,$pow2);
61f5c3f5 1235 $cx;
027dc388 1236 }
1237
b3abae2a 1238sub _fac
1239 {
1240 # factorial of $x
1241 # ref to array, return ref to array
1242 my ($c,$cx) = @_;
1243
1244 if ((@$cx == 1) && ($cx->[0] <= 2))
1245 {
1246 $cx->[0] = 1 * ($cx->[0]||1); # 0,1 => 1, 2 => 2
1247 return $cx;
1248 }
1249
1250 # go forward until $base is exceeded
1251 # limit is either $x or $base (x == 100 means as result too high)
1252 my $steps = 100; $steps = $cx->[0] if @$cx == 1;
1253 my $r = 2; my $cf = 3; my $step = 1; my $last = $r;
1254 while ($r < $BASE && $step < $steps)
1255 {
1256 $last = $r; $r *= $cf++; $step++;
1257 }
1258 if ((@$cx == 1) && ($step == $cx->[0]))
1259 {
1260 # completely done
1261 $cx = [$last];
1262 return $cx;
1263 }
1264 my $n = _copy($c,$cx);
1265 $cx = [$last];
1266
1267 #$cx = _one();
1268 while (!(@$n == 1 && $n->[0] == $step))
1269 {
1270 _mul($c,$cx,$n); _dec($c,$n);
1271 }
1272 $cx;
1273 }
1274
1275use constant DEBUG => 0;
1276
1277my $steps = 0;
1278
1279sub steps { $steps };
1280
1281sub _sqrt
0716bf9b 1282 {
394e6ffb 1283 # square-root of $x
1284 # ref to array, return ref to array
1285 my ($c,$x) = @_;
0716bf9b 1286
394e6ffb 1287 if (scalar @$x == 1)
1288 {
1289 # fit's into one Perl scalar
1290 $x->[0] = int(sqrt($x->[0]));
1291 return $x;
1292 }
1293 my $y = _copy($c,$x);
b3abae2a 1294 # hopefully _len/2 is < $BASE, the -1 is to always undershot the guess
1295 # since our guess will "grow"
1296 my $l = int((_len($c,$x)-1) / 2);
1297
1298 my $lastelem = $x->[-1]; # for guess
1299 my $elems = scalar @$x - 1;
1300 # not enough digits, but could have more?
1301 if ((length($lastelem) <= 3) && ($elems > 1))
1302 {
1303 # right-align with zero pad
1304 my $len = length($lastelem) & 1;
1305 print "$lastelem => " if DEBUG;
1306 $lastelem .= substr($x->[-2] . '0' x $BASE_LEN,0,$BASE_LEN);
1307 # former odd => make odd again, or former even to even again
1308 $lastelem = $lastelem / 10 if (length($lastelem) & 1) != $len;
1309 print "$lastelem\n" if DEBUG;
1310 }
0716bf9b 1311
61f5c3f5 1312 # construct $x (instead of _lsft($c,$x,$l,10)
1313 my $r = $l % $BASE_LEN; # 10000 00000 00000 00000 ($BASE_LEN=5)
1314 $l = int($l / $BASE_LEN);
b3abae2a 1315 print "l = $l " if DEBUG;
1316
1317 splice @$x,$l; # keep ref($x), but modify it
1318
1319 # we make the first part of the guess not '1000...0' but int(sqrt($lastelem))
1320 # that gives us:
1321 # 14400 00000 => sqrt(14400) => 120
1322 # 144000 000000 => sqrt(144000) => 379
1323
1324 # $x->[$l--] = int('1' . '0' x $r); # old way of guessing
1325 print "$lastelem (elems $elems) => " if DEBUG;
1326 $lastelem = $lastelem / 10 if ($elems & 1 == 1); # odd or even?
1327 my $g = sqrt($lastelem); $g =~ s/\.//; # 2.345 => 2345
1328 $r -= 1 if $elems & 1 == 0; # 70 => 7
1329
1330 # padd with zeros if result is too short
1331 $x->[$l--] = int(substr($g . '0' x $r,0,$r+1));
1332 print "now ",$x->[-1] if DEBUG;
1333 print " would have been ", int('1' . '0' x $r),"\n" if DEBUG;
1334
1335 # If @$x > 1, we could compute the second elem of the guess, too, to create
1336 # an even better guess. Not implemented yet.
1337 $x->[$l--] = 0 while ($l >= 0); # all other digits of guess are zero
61f5c3f5 1338
b3abae2a 1339 print "start x= ",${_str($c,$x)},"\n" if DEBUG;
394e6ffb 1340 my $two = _two();
1341 my $last = _zero();
1342 my $lastlast = _zero();
b3abae2a 1343 $steps = 0 if DEBUG;
394e6ffb 1344 while (_acmp($c,$last,$x) != 0 && _acmp($c,$lastlast,$x) != 0)
1345 {
b3abae2a 1346 $steps++ if DEBUG;
394e6ffb 1347 $lastlast = _copy($c,$last);
1348 $last = _copy($c,$x);
1349 _add($c,$x, _div($c,_copy($c,$y),$x));
1350 _div($c,$x, $two );
b3abae2a 1351 print " x= ",${_str($c,$x)},"\n" if DEBUG;
394e6ffb 1352 }
b3abae2a 1353 print "\nsteps in sqrt: $steps, " if DEBUG;
394e6ffb 1354 _dec($c,$x) if _acmp($c,$y,_mul($c,_copy($c,$x),$x)) < 0; # overshot?
b3abae2a 1355 print " final ",$x->[-1],"\n" if DEBUG;
394e6ffb 1356 $x;
0716bf9b 1357 }
1358
394e6ffb 1359##############################################################################
1360# binary stuff
0716bf9b 1361
394e6ffb 1362sub _and
1363 {
1364 my ($c,$x,$y) = @_;
0716bf9b 1365
394e6ffb 1366 # the shortcut makes equal, large numbers _really_ fast, and makes only a
1367 # very small performance drop for small numbers (e.g. something with less
1368 # than 32 bit) Since we optimize for large numbers, this is enabled.
1369 return $x if _acmp($c,$x,$y) == 0; # shortcut
0716bf9b 1370
394e6ffb 1371 my $m = _one(); my ($xr,$yr);
1372 my $mask = $AND_MASK;
1373
1374 my $x1 = $x;
1375 my $y1 = _copy($c,$y); # make copy
1376 $x = _zero();
1377 my ($b,$xrr,$yrr);
1378 use integer;
1379 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
1380 {
1381 ($x1, $xr) = _div($c,$x1,$mask);
1382 ($y1, $yr) = _div($c,$y1,$mask);
1383
1384 # make ints() from $xr, $yr
1385 # this is when the AND_BITS are greater tahn $BASE and is slower for
1386 # small (<256 bits) numbers, but faster for large numbers. Disabled
1387 # due to KISS principle
1388
1389# $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1390# $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
1391# _add($c,$x, _mul($c, _new( $c, \($xrr & $yrr) ), $m) );
1392
61f5c3f5 1393 # 0+ due to '&' doesn't work in strings
1394 _add($c,$x, _mul($c, [ 0+$xr->[0] & 0+$yr->[0] ], $m) );
394e6ffb 1395 _mul($c,$m,$mask);
1396 }
1397 $x;
0716bf9b 1398 }
1399
394e6ffb 1400sub _xor
0716bf9b 1401 {
394e6ffb 1402 my ($c,$x,$y) = @_;
1403
1404 return _zero() if _acmp($c,$x,$y) == 0; # shortcut (see -and)
1405
1406 my $m = _one(); my ($xr,$yr);
1407 my $mask = $XOR_MASK;
1408
1409 my $x1 = $x;
1410 my $y1 = _copy($c,$y); # make copy
1411 $x = _zero();
1412 my ($b,$xrr,$yrr);
1413 use integer;
1414 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
0716bf9b 1415 {
394e6ffb 1416 ($x1, $xr) = _div($c,$x1,$mask);
1417 ($y1, $yr) = _div($c,$y1,$mask);
1418 # make ints() from $xr, $yr (see _and())
1419 #$b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1420 #$b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
1421 #_add($c,$x, _mul($c, _new( $c, \($xrr ^ $yrr) ), $m) );
61f5c3f5 1422
1423 # 0+ due to '^' doesn't work in strings
1424 _add($c,$x, _mul($c, [ 0+$xr->[0] ^ 0+$yr->[0] ], $m) );
394e6ffb 1425 _mul($c,$m,$mask);
0716bf9b 1426 }
394e6ffb 1427 # the loop stops when the shorter of the two numbers is exhausted
1428 # the remainder of the longer one will survive bit-by-bit, so we simple
1429 # multiply-add it in
1430 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1);
1431 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1);
1432
1433 $x;
0716bf9b 1434 }
1435
394e6ffb 1436sub _or
0716bf9b 1437 {
394e6ffb 1438 my ($c,$x,$y) = @_;
0716bf9b 1439
394e6ffb 1440 return $x if _acmp($c,$x,$y) == 0; # shortcut (see _and)
0716bf9b 1441
394e6ffb 1442 my $m = _one(); my ($xr,$yr);
1443 my $mask = $OR_MASK;
0716bf9b 1444
394e6ffb 1445 my $x1 = $x;
1446 my $y1 = _copy($c,$y); # make copy
1447 $x = _zero();
1448 my ($b,$xrr,$yrr);
1449 use integer;
1450 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
1451 {
1452 ($x1, $xr) = _div($c,$x1,$mask);
1453 ($y1, $yr) = _div($c,$y1,$mask);
1454 # make ints() from $xr, $yr (see _and())
1455# $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1456# $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
1457# _add($c,$x, _mul($c, _new( $c, \($xrr | $yrr) ), $m) );
1458
61f5c3f5 1459 # 0+ due to '|' doesn't work in strings
1460 _add($c,$x, _mul($c, [ 0+$xr->[0] | 0+$yr->[0] ], $m) );
394e6ffb 1461 _mul($c,$m,$mask);
1462 }
1463 # the loop stops when the shorter of the two numbers is exhausted
1464 # the remainder of the longer one will survive bit-by-bit, so we simple
1465 # multiply-add it in
1466 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1);
1467 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1);
1468
1469 $x;
0716bf9b 1470 }
1471
61f5c3f5 1472sub _as_hex
1473 {
1474 # convert a decimal number to hex (ref to array, return ref to string)
1475 my ($c,$x) = @_;
1476
1477 my $x1 = _copy($c,$x);
1478
1479 my $es = '';
1480 my $xr;
1481 my $x10000 = [ 0x10000 ];
1482 while (! _is_zero($c,$x1))
1483 {
1484 ($x1, $xr) = _div($c,$x1,$x10000);
1485 $es .= unpack('h4',pack('v',$xr->[0]));
1486 }
1487 $es = reverse $es;
1488 $es =~ s/^[0]+//; # strip leading zeros
1489 $es = '0x' . $es;
1490 \$es;
1491 }
1492
1493sub _as_bin
1494 {
1495 # convert a decimal number to bin (ref to array, return ref to string)
1496 my ($c,$x) = @_;
1497
1498 my $x1 = _copy($c,$x);
1499
1500 my $es = '';
1501 my $xr;
1502 my $x10000 = [ 0x10000 ];
1503 while (! _is_zero($c,$x1))
1504 {
1505 ($x1, $xr) = _div($c,$x1,$x10000);
1506 $es .= unpack('b16',pack('v',$xr->[0]));
1507 }
1508 $es = reverse $es;
1509 $es =~ s/^[0]+//; # strip leading zeros
1510 $es = '0b' . $es;
1511 \$es;
1512 }
1513
394e6ffb 1514sub _from_hex
0716bf9b 1515 {
394e6ffb 1516 # convert a hex number to decimal (ref to string, return ref to array)
1517 my ($c,$hs) = @_;
0716bf9b 1518
394e6ffb 1519 my $mul = _one();
1520 my $m = [ 0x10000 ]; # 16 bit at a time
1521 my $x = _zero();
0716bf9b 1522
61f5c3f5 1523 my $len = length($$hs)-2;
394e6ffb 1524 $len = int($len/4); # 4-digit parts, w/o '0x'
1525 my $val; my $i = -4;
1526 while ($len >= 0)
1527 {
1528 $val = substr($$hs,$i,4);
1529 $val =~ s/^[+-]?0x// if $len == 0; # for last part only because
1530 $val = hex($val); # hex does not like wrong chars
1531 $i -= 4; $len --;
1532 _add ($c, $x, _mul ($c, [ $val ], $mul ) ) if $val != 0;
1533 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul
1534 }
1535 $x;
1536 }
1537
1538sub _from_bin
0716bf9b 1539 {
394e6ffb 1540 # convert a hex number to decimal (ref to string, return ref to array)
1541 my ($c,$bs) = @_;
0716bf9b 1542
13a12e00 1543 # instead of converting 8 bit at a time, it is faster to convert the
1544 # number to hex, and then call _from_hex.
1545
1546 my $hs = $$bs;
1547 $hs =~ s/^[+-]?0b//; # remove sign and 0b
1548 my $l = length($hs); # bits
1549 $hs = '0' x (8-($l % 8)) . $hs if ($l % 8) != 0; # padd left side w/ 0
1550 my $h = unpack('H*', pack ('B*', $hs)); # repack as hex
1551 return $c->_from_hex(\('0x'.$h));
1552
394e6ffb 1553 my $mul = _one();
1554 my $m = [ 0x100 ]; # 8 bit at a time
1555 my $x = _zero();
0716bf9b 1556
61f5c3f5 1557 my $len = length($$bs)-2;
394e6ffb 1558 $len = int($len/8); # 4-digit parts, w/o '0x'
1559 my $val; my $i = -8;
1560 while ($len >= 0)
0716bf9b 1561 {
394e6ffb 1562 $val = substr($$bs,$i,8);
1563 $val =~ s/^[+-]?0b// if $len == 0; # for last part only
1564
394e6ffb 1565 $val = ord(pack('B8',substr('00000000'.$val,-8,8)));
1566
1567 $i -= 8; $len --;
1568 _add ($c, $x, _mul ($c, [ $val ], $mul ) ) if $val != 0;
1569 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul
0716bf9b 1570 }
394e6ffb 1571 $x;
0716bf9b 1572 }
1573
394e6ffb 1574##############################################################################
1575##############################################################################
1576
0716bf9b 15771;
1578__END__
1579
1580=head1 NAME
1581
1582Math::BigInt::Calc - Pure Perl module to support Math::BigInt
1583
1584=head1 SYNOPSIS
1585
ee15d750 1586Provides support for big integer calculations. Not intended to be used by other
1587modules (except Math::BigInt::Cached). Other modules which sport the same
1588functions can also be used to support Math::Bigint, like Math::BigInt::Pari.
0716bf9b 1589
1590=head1 DESCRIPTION
1591
027dc388 1592In order to allow for multiple big integer libraries, Math::BigInt was
1593rewritten to use library modules for core math routines. Any module which
1594follows the same API as this can be used instead by using the following:
0716bf9b 1595
ee15d750 1596 use Math::BigInt lib => 'libname';
0716bf9b 1597
027dc388 1598'libname' is either the long name ('Math::BigInt::Pari'), or only the short
1599version like 'Pari'.
1600
0716bf9b 1601=head1 EXPORT
1602
027dc388 1603The following functions MUST be defined in order to support the use by
1604Math::BigInt:
0716bf9b 1605
1606 _new(string) return ref to new object from ref to decimal string
1607 _zero() return a new object with value 0
1608 _one() return a new object with value 1
1609
1610 _str(obj) return ref to a string representing the object
1611 _num(obj) returns a Perl integer/floating point number
1612 NOTE: because of Perl numeric notation defaults,
1613 the _num'ified obj may lose accuracy due to
1614 machine-dependend floating point size limitations
1615
1616 _add(obj,obj) Simple addition of two objects
1617 _mul(obj,obj) Multiplication of two objects
1618 _div(obj,obj) Division of the 1st object by the 2nd
b22b3e31 1619 In list context, returns (result,remainder).
1620 NOTE: this is integer math, so no
1621 fractional part will be returned.
1622 _sub(obj,obj) Simple subtraction of 1 object from another
0716bf9b 1623 a third, optional parameter indicates that the params
1624 are swapped. In this case, the first param needs to
1625 be preserved, while you can destroy the second.
1626 sub (x,y,1) => return x - y and keep x intact!
e745a66c 1627 _dec(obj) decrement object by one (input is garant. to be > 0)
1628 _inc(obj) increment object by one
1629
0716bf9b 1630
1631 _acmp(obj,obj) <=> operator for objects (return -1, 0 or 1)
1632
1633 _len(obj) returns count of the decimal digits of the object
1634 _digit(obj,n) returns the n'th decimal digit of object
1635
1636 _is_one(obj) return true if argument is +1
1637 _is_zero(obj) return true if argument is 0
1638 _is_even(obj) return true if argument is even (0,2,4,6..)
1639 _is_odd(obj) return true if argument is odd (1,3,5,7..)
1640
1641 _copy return a ref to a true copy of the object
1642
1643 _check(obj) check whether internal representation is still intact
1644 return 0 for ok, otherwise error message as string
1645
bd05a461 1646The following functions are optional, and can be defined if the underlying lib
027dc388 1647has a fast way to do them. If undefined, Math::BigInt will use pure Perl (hence
1648slow) fallback routines to emulate these:
0716bf9b 1649
1650 _from_hex(str) return ref to new object from ref to hexadecimal string
1651 _from_bin(str) return ref to new object from ref to binary string
1652
ee15d750 1653 _as_hex(str) return ref to scalar string containing the value as
1654 unsigned hex string, with the '0x' prepended.
1655 Leading zeros must be stripped.
1656 _as_bin(str) Like as_hex, only as binary string containing only
1657 zeros and ones. Leading zeros must be stripped and a
1658 '0b' must be prepended.
1659
0716bf9b 1660 _rsft(obj,N,B) shift object in base B by N 'digits' right
dccbb853 1661 For unsupported bases B, return undef to signal failure
0716bf9b 1662 _lsft(obj,N,B) shift object in base B by N 'digits' left
dccbb853 1663 For unsupported bases B, return undef to signal failure
0716bf9b 1664
1665 _xor(obj1,obj2) XOR (bit-wise) object 1 with object 2
dccbb853 1666 Note: XOR, AND and OR pad with zeros if size mismatches
0716bf9b 1667 _and(obj1,obj2) AND (bit-wise) object 1 with object 2
1668 _or(obj1,obj2) OR (bit-wise) object 1 with object 2
1669
dccbb853 1670 _mod(obj,obj) Return remainder of div of the 1st by the 2nd object
394e6ffb 1671 _sqrt(obj) return the square root of object (truncate to int)
b3abae2a 1672 _fac(obj) return factorial of object 1 (1*2*3*4..)
0716bf9b 1673 _pow(obj,obj) return object 1 to the power of object 2
1674 _gcd(obj,obj) return Greatest Common Divisor of two objects
1675
b22b3e31 1676 _zeros(obj) return number of trailing decimal zeros
0716bf9b 1677
b22b3e31 1678Input strings come in as unsigned but with prefix (i.e. as '123', '0xabc'
0716bf9b 1679or '0b1101').
1680
b22b3e31 1681Testing of input parameter validity is done by the caller, so you need not
574bacfe 1682worry about underflow (f.i. in C<_sub()>, C<_dec()>) nor about division by
1683zero or similar cases.
1684
1685The first parameter can be modified, that includes the possibility that you
1686return a reference to a completely different object instead. Although keeping
dccbb853 1687the reference and just changing it's contents is prefered over creating and
1688returning a different reference.
574bacfe 1689
1690Return values are always references to objects or strings. Exceptions are
1691C<_lsft()> and C<_rsft()>, which return undef if they can not shift the
027dc388 1692argument. This is used to delegate shifting of bases different than the one
1693you can support back to Math::BigInt, which will use some generic code to
1694calculate the result.
574bacfe 1695
1696=head1 WRAP YOUR OWN
1697
1698If you want to port your own favourite c-lib for big numbers to the
1699Math::BigInt interface, you can take any of the already existing modules as
1700a rough guideline. You should really wrap up the latest BigInt and BigFloat
bd05a461 1701testsuites with your module, and replace in them any of the following:
574bacfe 1702
1703 use Math::BigInt;
1704
bd05a461 1705by this:
574bacfe 1706
1707 use Math::BigInt lib => 'yourlib';
1708
1709This way you ensure that your library really works 100% within Math::BigInt.
0716bf9b 1710
1711=head1 LICENSE
1712
1713This program is free software; you may redistribute it and/or modify it under
1714the same terms as Perl itself.
1715
1716=head1 AUTHORS
1717
1718Original math code by Mark Biggar, rewritten by Tels L<http://bloodgate.com/>
1719in late 2000, 2001.
1720Seperated from BigInt and shaped API with the help of John Peacock.
1721
1722=head1 SEE ALSO
1723
ee15d750 1724L<Math::BigInt>, L<Math::BigFloat>, L<Math::BigInt::BitVect>,
1725L<Math::BigInt::GMP>, L<Math::BigInt::Cached> and L<Math::BigInt::Pari>.
0716bf9b 1726
1727=cut