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