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