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