Add IPC::Run 0.80.
[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
b282a552 7use vars qw/$VERSION/;
0716bf9b 8
da219303 9$VERSION = '0.45';
0716bf9b 10
11# Package to store unsigned big integers in decimal and do math with them
12
13# Internally the numbers are stored in an array with at least 1 element, no
027dc388 14# leading zero parts (except the first) and in base 1eX where X is determined
15# automatically at loading time to be the maximum possible value
0716bf9b 16
17# todo:
18# - fully remove funky $# stuff (maybe)
0716bf9b 19
20# USE_MUL: due to problems on certain os (os390, posix-bc) "* 1e-5" is used
ee15d750 21# instead of "/ 1e5" at some places, (marked with USE_MUL). Other platforms
22# BS2000, some Crays need USE_DIV instead.
bd05a461 23# The BEGIN block is used to determine which of the two variants gives the
24# correct result.
0716bf9b 25
990fb837 26# Beware of things like:
27# $i = $i * $y + $car; $car = int($i / $MBASE); $i = $i % $MBASE;
93c87d9d 28# This works on x86, but fails on ARM (SA1100, iPAQ) due to whoknows what
990fb837 29# reasons. So, use this instead (slower, but correct):
30# $i = $i * $y + $car; $car = int($i / $MBASE); $i -= $MBASE * $car;
31
0716bf9b 32##############################################################################
33# global constants, flags and accessory
9b924220 34
35# announce that we are compatible with MBI v1.70 and up
36sub api_version () { 1; }
0716bf9b 37
38# constants for easier life
2d2b2744 39my ($MBASE,$BASE,$RBASE,$BASE_LEN,$MAX_VAL,$BASE_LEN_SMALL);
394e6ffb 40my ($AND_BITS,$XOR_BITS,$OR_BITS);
41my ($AND_MASK,$XOR_MASK,$OR_MASK);
ee15d750 42
43sub _base_len
44 {
dccbb853 45 # set/get the BASE_LEN and assorted other, connected values
46 # used only be the testsuite, set is used only by the BEGIN block below
394e6ffb 47 shift;
48
ee15d750 49 my $b = shift;
50 if (defined $b)
51 {
61f5c3f5 52 # find whether we can use mul or div or none in mul()/div()
53 # (in last case reduce BASE_LEN_SMALL)
54 $BASE_LEN_SMALL = $b+1;
55 my $caught = 0;
56 while (--$BASE_LEN_SMALL > 5)
394e6ffb 57 {
61f5c3f5 58 $MBASE = int("1e".$BASE_LEN_SMALL);
59 $RBASE = abs('1e-'.$BASE_LEN_SMALL); # see USE_MUL
394e6ffb 60 $caught = 0;
61f5c3f5 61 $caught += 1 if (int($MBASE * $RBASE) != 1); # should be 1
62 $caught += 2 if (int($MBASE / $MBASE) != 1); # should be 1
394e6ffb 63 last if $caught != 3;
64 }
61f5c3f5 65 # BASE_LEN is used for anything else than mul()/div()
66 $BASE_LEN = $BASE_LEN_SMALL;
67 $BASE_LEN = shift if (defined $_[0]); # one more arg?
ee15d750 68 $BASE = int("1e".$BASE_LEN);
61f5c3f5 69
61f5c3f5 70 $MBASE = int("1e".$BASE_LEN_SMALL);
71 $RBASE = abs('1e-'.$BASE_LEN_SMALL); # see USE_MUL
72 $MAX_VAL = $MBASE-1;
b68b7ab1 73
74 # avoid redefinitions
75
b05afeb3 76 undef &_mul;
77 undef &_div;
1ddff52a 78
990fb837 79 # $caught & 1 != 0 => cannot use MUL
80 # $caught & 2 != 0 => cannot use DIV
81 # The parens around ($caught & 1) were important, indeed, if we would use
82 # & here.
83 if ($caught == 2) # 2
ee15d750 84 {
990fb837 85 # must USE_MUL since we cannot use DIV
ee15d750 86 *{_mul} = \&_mul_use_mul;
87 *{_div} = \&_div_use_mul;
88 }
990fb837 89 else # 0 or 1
ee15d750 90 {
ee15d750 91 # can USE_DIV instead
92 *{_mul} = \&_mul_use_div;
93 *{_div} = \&_div_use_div;
94 }
95 }
61f5c3f5 96 return $BASE_LEN unless wantarray;
97 return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN_SMALL, $MAX_VAL);
ee15d750 98 }
574bacfe 99
03874afe 100sub _new
101 {
102 # (ref to string) return ref to num_array
103 # Convert a number from string format (without sign) to internal base
104 # 1ex format. Assumes normalized value as input.
105 my $il = length($_[1])-1;
106
107 # < BASE_LEN due len-1 above
108 return [ int($_[1]) ] if $il < $BASE_LEN; # shortcut for short numbers
109
110 # this leaves '00000' instead of int 0 and will be corrected after any op
111 [ reverse(unpack("a" . ($il % $BASE_LEN+1)
112 . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ];
113 }
114
574bacfe 115BEGIN
116 {
bd05a461 117 # from Daniel Pfeiffer: determine largest group of digits that is precisely
574bacfe 118 # multipliable with itself plus carry
dccbb853 119 # Test now changed to expect the proper pattern, not a result off by 1 or 2
120 my ($e, $num) = 3; # lowest value we will use is 3+1-1 = 3
bd05a461 121 do
122 {
123 $num = ('9' x ++$e) + 0;
394e6ffb 124 $num *= $num + 1.0;
394e6ffb 125 } while ("$num" =~ /9{$e}0{$e}/); # must be a certain pattern
126 $e--; # last test failed, so retract one step
127 # the limits below brush the problems with the test above under the rug:
128 # the test should be able to find the proper $e automatically
129 $e = 5 if $^O =~ /^uts/; # UTS get's some special treatment
130 $e = 5 if $^O =~ /^unicos/; # unicos is also problematic (6 seems to work
131 # there, but we play safe)
07d34614 132 $e = 5 if $] < 5.006; # cap, for older Perls
2e507a43 133 $e = 7 if $e > 7; # cap, for VMS, OS/390 and other 64 bit systems
134 # 8 fails inside random testsuite, so take 7
394e6ffb 135
03874afe 136 __PACKAGE__->_base_len($e); # set and store
394e6ffb 137
b68b7ab1 138 use integer;
394e6ffb 139 # find out how many bits _and, _or and _xor can take (old default = 16)
140 # I don't think anybody has yet 128 bit scalars, so let's play safe.
394e6ffb 141 local $^W = 0; # don't warn about 'nonportable number'
c38b2de2 142 $AND_BITS = 15; $XOR_BITS = 15; $OR_BITS = 15;
394e6ffb 143
144 # find max bits, we will not go higher than numberofbits that fit into $BASE
145 # to make _and etc simpler (and faster for smaller, slower for large numbers)
146 my $max = 16;
147 while (2 ** $max < $BASE) { $max++; }
1ddff52a 148 {
149 no integer;
150 $max = 16 if $] < 5.006; # older Perls might not take >16 too well
151 }
394e6ffb 152 my ($x,$y,$z);
153 do {
154 $AND_BITS++;
155 $x = oct('0b' . '1' x $AND_BITS); $y = $x & $x;
156 $z = (2 ** $AND_BITS) - 1;
157 } while ($AND_BITS < $max && $x == $z && $y == $x);
158 $AND_BITS --; # retreat one step
159 do {
160 $XOR_BITS++;
161 $x = oct('0b' . '1' x $XOR_BITS); $y = $x ^ 0;
162 $z = (2 ** $XOR_BITS) - 1;
163 } while ($XOR_BITS < $max && $x == $z && $y == $x);
164 $XOR_BITS --; # retreat one step
165 do {
166 $OR_BITS++;
167 $x = oct('0b' . '1' x $OR_BITS); $y = $x | $x;
168 $z = (2 ** $OR_BITS) - 1;
169 } while ($OR_BITS < $max && $x == $z && $y == $x);
170 $OR_BITS --; # retreat one step
171
9b924220 172 $AND_MASK = __PACKAGE__->_new( ( 2 ** $AND_BITS ));
173 $XOR_MASK = __PACKAGE__->_new( ( 2 ** $XOR_BITS ));
174 $OR_MASK = __PACKAGE__->_new( ( 2 ** $OR_BITS ));
394e6ffb 175 }
0716bf9b 176
03874afe 177###############################################################################
178
0716bf9b 179sub _zero
180 {
181 # create a zero
61f5c3f5 182 [ 0 ];
0716bf9b 183 }
184
185sub _one
186 {
187 # create a one
61f5c3f5 188 [ 1 ];
0716bf9b 189 }
190
027dc388 191sub _two
192 {
1ddff52a 193 # create a two (used internally for shifting)
61f5c3f5 194 [ 2 ];
027dc388 195 }
196
9b924220 197sub _ten
198 {
199 # create a 10 (used internally for shifting)
200 [ 10 ];
201 }
202
0716bf9b 203sub _copy
204 {
091c87b1 205 # make a true copy
61f5c3f5 206 [ @{$_[1]} ];
0716bf9b 207 }
208
bd05a461 209# catch and throw away
210sub import { }
211
0716bf9b 212##############################################################################
213# convert back to string and number
214
215sub _str
216 {
217 # (ref to BINT) return num_str
218 # Convert number from internal base 100000 format to string format.
219 # internal format is always normalized (no leading zeros, "-0" => "+0")
574bacfe 220 my $ar = $_[1];
61f5c3f5 221
b68b7ab1 222 my $l = scalar @$ar; # number of parts
223 if ($l < 1) # should not happen
224 {
225 require Carp;
226 Carp::croak("$_[1] has no elements");
227 }
61f5c3f5 228
b68b7ab1 229 my $ret = "";
0716bf9b 230 # handle first one different to strip leading zeros from it (there are no
231 # leading zero parts in internal representation)
61f5c3f5 232 $l --; $ret .= int($ar->[$l]); $l--;
0716bf9b 233 # Interestingly, the pre-padd method uses more time
091c87b1 234 # the old grep variant takes longer (14 vs. 10 sec)
574bacfe 235 my $z = '0' x ($BASE_LEN-1);
0716bf9b 236 while ($l >= 0)
237 {
574bacfe 238 $ret .= substr($z.$ar->[$l],-$BASE_LEN); # fastest way I could think of
0716bf9b 239 $l--;
240 }
9b924220 241 $ret;
0716bf9b 242 }
243
244sub _num
245 {
9b924220 246 # Make a number (scalar int/float) from a BigInt object
574bacfe 247 my $x = $_[1];
9b924220 248
249 return 0+$x->[0] if scalar @$x == 1; # below $BASE
0716bf9b 250 my $fac = 1;
251 my $num = 0;
252 foreach (@$x)
253 {
254 $num += $fac*$_; $fac *= $BASE;
255 }
61f5c3f5 256 $num;
0716bf9b 257 }
258
259##############################################################################
260# actual math code
261
262sub _add
263 {
264 # (ref to int_num_array, ref to int_num_array)
574bacfe 265 # routine to add two base 1eX numbers
0716bf9b 266 # stolen from Knuth Vol 2 Algorithm A pg 231
b22b3e31 267 # there are separate routines to add and sub as per Knuth pg 233
0716bf9b 268 # This routine clobbers up array x, but not y.
269
574bacfe 270 my ($c,$x,$y) = @_;
b3abae2a 271
272 return $x if (@$y == 1) && $y->[0] == 0; # $x + 0 => $x
273 if ((@$x == 1) && $x->[0] == 0) # 0 + $y => $y->copy
274 {
275 # twice as slow as $x = [ @$y ], but necc. to retain $x as ref :(
276 @$x = @$y; return $x;
277 }
0716bf9b 278
279 # for each in Y, add Y to X and carry. If after that, something is left in
280 # X, foreach in X add carry to X and then return X, carry
091c87b1 281 # Trades one "$j++" for having to shift arrays
0716bf9b 282 my $i; my $car = 0; my $j = 0;
283 for $i (@$y)
284 {
e745a66c 285 $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0;
0716bf9b 286 $j++;
287 }
288 while ($car != 0)
289 {
290 $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0; $j++;
291 }
61f5c3f5 292 $x;
e745a66c 293 }
294
295sub _inc
296 {
297 # (ref to int_num_array, ref to int_num_array)
091c87b1 298 # Add 1 to $x, modify $x in place
e745a66c 299 my ($c,$x) = @_;
300
301 for my $i (@$x)
302 {
303 return $x if (($i += 1) < $BASE); # early out
61f5c3f5 304 $i = 0; # overflow, next
e745a66c 305 }
ae161977 306 push @$x,1 if (($x->[-1] || 0) == 0); # last overflowed, so extend
61f5c3f5 307 $x;
e745a66c 308 }
309
310sub _dec
311 {
312 # (ref to int_num_array, ref to int_num_array)
091c87b1 313 # Sub 1 from $x, modify $x in place
e745a66c 314 my ($c,$x) = @_;
315
61f5c3f5 316 my $MAX = $BASE-1; # since MAX_VAL based on MBASE
e745a66c 317 for my $i (@$x)
318 {
319 last if (($i -= 1) >= 0); # early out
091c87b1 320 $i = $MAX; # underflow, next
e745a66c 321 }
091c87b1 322 pop @$x if $x->[-1] == 0 && @$x > 1; # last underflowed (but leave 0)
61f5c3f5 323 $x;
0716bf9b 324 }
325
326sub _sub
327 {
9393ace2 328 # (ref to int_num_array, ref to int_num_array, swap)
574bacfe 329 # subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
56b9c951 330 # subtract Y from X by modifying x in place
574bacfe 331 my ($c,$sx,$sy,$s) = @_;
0716bf9b 332
333 my $car = 0; my $i; my $j = 0;
334 if (!$s)
335 {
0716bf9b 336 for $i (@$sx)
337 {
338 last unless defined $sy->[$j] || $car;
0716bf9b 339 $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0); $j++;
0716bf9b 340 }
341 # might leave leading zeros, so fix that
394e6ffb 342 return __strip_zeros($sx);
0716bf9b 343 }
394e6ffb 344 for $i (@$sx)
0716bf9b 345 {
07d34614 346 # we can't do an early out if $x is < than $y, since we
56b9c951 347 # need to copy the high chunks from $y. Found by Bob Mathews.
348 #last unless defined $sy->[$j] || $car;
394e6ffb 349 $sy->[$j] += $BASE
350 if $car = (($sy->[$j] = $i-($sy->[$j]||0) - $car) < 0);
351 $j++;
0716bf9b 352 }
394e6ffb 353 # might leave leading zeros, so fix that
354 __strip_zeros($sy);
0716bf9b 355 }
356
ee15d750 357sub _mul_use_mul
0716bf9b 358 {
9393ace2 359 # (ref to int_num_array, ref to int_num_array)
0716bf9b 360 # multiply two numbers in internal representation
b22b3e31 361 # modifies first arg, second need not be different from first
574bacfe 362 my ($c,$xv,$yv) = @_;
dccbb853 363
990fb837 364 if (@$yv == 1)
b3abae2a 365 {
990fb837 366 # shortcut for two very short numbers (improved by Nathan Zook)
367 # works also if xv and yv are the same reference, and handles also $x == 0
368 if (@$xv == 1)
369 {
370 if (($xv->[0] *= $yv->[0]) >= $MBASE)
371 {
372 $xv->[0] = $xv->[0] - ($xv->[1] = int($xv->[0] * $RBASE)) * $MBASE;
373 };
374 return $xv;
375 }
376 # $x * 0 => 0
377 if ($yv->[0] == 0)
378 {
379 @$xv = (0);
380 return $xv;
381 }
382 # multiply a large number a by a single element one, so speed up
383 my $y = $yv->[0]; my $car = 0;
384 foreach my $i (@$xv)
385 {
386 $i = $i * $y + $car; $car = int($i * $RBASE); $i -= $car * $MBASE;
387 }
388 push @$xv, $car if $car != 0;
b3abae2a 389 return $xv;
390 }
990fb837 391 # shortcut for result $x == 0 => result = 0
392 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) );
b3abae2a 393
0716bf9b 394 # since multiplying $x with $x fails, make copy in this case
d614cd8b 395 $yv = [@$xv] if $xv == $yv; # same references?
9393ace2 396
61f5c3f5 397 my @prod = (); my ($prod,$car,$cty,$xi,$yi);
398
0716bf9b 399 for $xi (@$xv)
400 {
401 $car = 0; $cty = 0;
574bacfe 402
403 # slow variant
404# for $yi (@$yv)
405# {
406# $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
407# $prod[$cty++] =
61f5c3f5 408# $prod - ($car = int($prod * RBASE)) * $MBASE; # see USE_MUL
574bacfe 409# }
410# $prod[$cty] += $car if $car; # need really to check for 0?
411# $xi = shift @prod;
412
413 # faster variant
414 # looping through this if $xi == 0 is silly - so optimize it away!
415 $xi = (shift @prod || 0), next if $xi == 0;
0716bf9b 416 for $yi (@$yv)
417 {
418 $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
574bacfe 419## this is actually a tad slower
420## $prod = $prod[$cty]; $prod += ($car + $xi * $yi); # no ||0 here
0716bf9b 421 $prod[$cty++] =
61f5c3f5 422 $prod - ($car = int($prod * $RBASE)) * $MBASE; # see USE_MUL
0716bf9b 423 }
424 $prod[$cty] += $car if $car; # need really to check for 0?
027dc388 425 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy
0716bf9b 426 }
0716bf9b 427 push @$xv, @prod;
990fb837 428 __strip_zeros($xv);
61f5c3f5 429 $xv;
0716bf9b 430 }
431
ee15d750 432sub _mul_use_div
433 {
9393ace2 434 # (ref to int_num_array, ref to int_num_array)
ee15d750 435 # multiply two numbers in internal representation
436 # modifies first arg, second need not be different from first
437 my ($c,$xv,$yv) = @_;
438
990fb837 439 if (@$yv == 1)
b3abae2a 440 {
990fb837 441 # shortcut for two small numbers, also handles $x == 0
442 if (@$xv == 1)
443 {
444 # shortcut for two very short numbers (improved by Nathan Zook)
445 # works also if xv and yv are the same reference, and handles also $x == 0
446 if (($xv->[0] *= $yv->[0]) >= $MBASE)
447 {
448 $xv->[0] =
449 $xv->[0] - ($xv->[1] = int($xv->[0] / $MBASE)) * $MBASE;
450 };
451 return $xv;
452 }
453 # $x * 0 => 0
454 if ($yv->[0] == 0)
455 {
456 @$xv = (0);
457 return $xv;
458 }
459 # multiply a large number a by a single element one, so speed up
460 my $y = $yv->[0]; my $car = 0;
461 foreach my $i (@$xv)
462 {
463 $i = $i * $y + $car; $car = int($i / $MBASE); $i -= $car * $MBASE;
464 }
465 push @$xv, $car if $car != 0;
b3abae2a 466 return $xv;
467 }
990fb837 468 # shortcut for result $x == 0 => result = 0
469 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) );
b3abae2a 470
ee15d750 471 # since multiplying $x with $x fails, make copy in this case
d614cd8b 472 $yv = [@$xv] if $xv == $yv; # same references?
9393ace2 473
61f5c3f5 474 my @prod = (); my ($prod,$car,$cty,$xi,$yi);
ee15d750 475 for $xi (@$xv)
476 {
477 $car = 0; $cty = 0;
478 # looping through this if $xi == 0 is silly - so optimize it away!
479 $xi = (shift @prod || 0), next if $xi == 0;
480 for $yi (@$yv)
481 {
482 $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
483 $prod[$cty++] =
61f5c3f5 484 $prod - ($car = int($prod / $MBASE)) * $MBASE;
ee15d750 485 }
486 $prod[$cty] += $car if $car; # need really to check for 0?
027dc388 487 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy
ee15d750 488 }
489 push @$xv, @prod;
990fb837 490 __strip_zeros($xv);
61f5c3f5 491 $xv;
ee15d750 492 }
493
494sub _div_use_mul
0716bf9b 495 {
b22b3e31 496 # ref to array, ref to array, modify first array and return remainder if
0716bf9b 497 # in list context
aef458a0 498
499 # see comments in _div_use_div() for more explanations
500
574bacfe 501 my ($c,$x,$yorg) = @_;
aef458a0 502
503 # the general div algorithmn here is about O(N*N) and thus quite slow, so
504 # we first check for some special cases and use shortcuts to handle them.
0716bf9b 505
aef458a0 506 # This works, because we store the numbers in a chunked format where each
507 # element contains 5..7 digits (depending on system).
508
509 # if both numbers have only one element:
61f5c3f5 510 if (@$x == 1 && @$yorg == 1)
511 {
13a12e00 512 # shortcut, $yorg and $x are two small numbers
61f5c3f5 513 if (wantarray)
514 {
515 my $r = [ $x->[0] % $yorg->[0] ];
516 $x->[0] = int($x->[0] / $yorg->[0]);
517 return ($x,$r);
518 }
519 else
520 {
521 $x->[0] = int($x->[0] / $yorg->[0]);
522 return $x;
523 }
524 }
aef458a0 525
526 # if x has more than one, but y has only one element:
28df3e88 527 if (@$yorg == 1)
528 {
529 my $rem;
530 $rem = _mod($c,[ @$x ],$yorg) if wantarray;
13a12e00 531
28df3e88 532 # shortcut, $y is < $BASE
533 my $j = scalar @$x; my $r = 0;
534 my $y = $yorg->[0]; my $b;
535 while ($j-- > 0)
536 {
537 $b = $r * $MBASE + $x->[$j];
538 $x->[$j] = int($b/$y);
539 $r = $b % $y;
540 }
541 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero
542 return ($x,$rem) if wantarray;
543 return $x;
544 }
0716bf9b 545
aef458a0 546 # now x and y have more than one element
547
548 # check whether y has more elements than x, if yet, the result will be 0
549 if (@$yorg > @$x)
550 {
551 my $rem;
552 $rem = [@$x] if wantarray; # make copy
553 splice (@$x,1); # keep ref to original array
554 $x->[0] = 0; # set to 0
555 return ($x,$rem) if wantarray; # including remainder?
556 return $x; # only x, which is [0] now
557 }
558 # check whether the numbers have the same number of elements, in that case
559 # the result will fit into one element and can be computed efficiently
560 if (@$yorg == @$x)
561 {
562 my $rem;
563 # if $yorg has more digits than $x (it's leading element is longer than
564 # the one from $x), the result will also be 0:
565 if (length(int($yorg->[-1])) > length(int($x->[-1])))
566 {
567 $rem = [@$x] if wantarray; # make copy
568 splice (@$x,1); # keep ref to org array
569 $x->[0] = 0; # set to 0
570 return ($x,$rem) if wantarray; # including remainder?
571 return $x;
572 }
573 # now calculate $x / $yorg
574 if (length(int($yorg->[-1])) == length(int($x->[-1])))
575 {
b68b7ab1 576
577 # We take a shortcut here, because the result must be
578 # between 1 and MAX_VAL (e.g. one element) and rem is not wanted.
579 if (!wantarray)
580 {
da219303 581 # fit's into one Perl scalar, so result can be computed directly
582 # cannot use int() here, because it rounds wrongly on some systems
583 #$x->[0] = int($x->[-1] / $yorg->[-1]);
584 # round to 8 digits, then truncate result to integer
585 $x->[0] = int ( sprintf ("%.8f", $x->[-1] / $yorg->[-1]) );
b68b7ab1 586 splice(@$x,1); # keep single element
587 return $x;
588 }
589
590 # wantarray: return (x,rem)
591 # same length, so make full compare
592
aef458a0 593 my $a = 0; my $j = scalar @$x - 1;
594 # manual way (abort if unequal, good for early ne)
595 while ($j >= 0)
596 {
597 last if ($a = $x->[$j] - $yorg->[$j]); $j--;
598 }
599 # $a contains the result of the compare between X and Y
b68b7ab1 600 # a < 0: x < y, a == 0: x == y, a > 0: x > y
aef458a0 601 if ($a <= 0)
602 {
b68b7ab1 603 $rem = [ 0 ]; # a = 0 => x == y => rem 0
604 $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x
605 splice(@$x,1); # keep single element
606 $x->[0] = 0; # if $a < 0
607 $x->[0] = 1 if $a == 0; # $x == $y
608 return ($x,$rem);
aef458a0 609 }
b68b7ab1 610 # $x >= $y, so proceed normally
aef458a0 611 }
612 }
613
614 # all other cases:
615
d614cd8b 616 my $y = [ @$yorg ]; # always make copy to preserve
61f5c3f5 617
618 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);
619
620 $car = $bar = $prd = 0;
621 if (($dd = int($MBASE/($y->[-1]+1))) != 1)
0716bf9b 622 {
623 for $xi (@$x)
624 {
625 $xi = $xi * $dd + $car;
61f5c3f5 626 $xi -= ($car = int($xi * $RBASE)) * $MBASE; # see USE_MUL
0716bf9b 627 }
628 push(@$x, $car); $car = 0;
629 for $yi (@$y)
630 {
631 $yi = $yi * $dd + $car;
61f5c3f5 632 $yi -= ($car = int($yi * $RBASE)) * $MBASE; # see USE_MUL
0716bf9b 633 }
634 }
635 else
636 {
637 push(@$x, 0);
638 }
639 @q = (); ($v2,$v1) = @$y[-2,-1];
640 $v2 = 0 unless $v2;
641 while ($#$x > $#$y)
642 {
643 ($u2,$u1,$u0) = @$x[-3..-1];
644 $u2 = 0 unless $u2;
645 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
646 # if $v1 == 0;
aef458a0 647 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1));
61f5c3f5 648 --$q while ($v2*$q > ($u0*$MBASE+$u1-$q*$v1)*$MBASE+$u2);
0716bf9b 649 if ($q)
650 {
651 ($car, $bar) = (0,0);
652 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
653 {
654 $prd = $q * $y->[$yi] + $car;
61f5c3f5 655 $prd -= ($car = int($prd * $RBASE)) * $MBASE; # see USE_MUL
656 $x->[$xi] += $MBASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0));
0716bf9b 657 }
658 if ($x->[-1] < $car + $bar)
659 {
660 $car = 0; --$q;
661 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
662 {
61f5c3f5 663 $x->[$xi] -= $MBASE
aef458a0 664 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $MBASE));
0716bf9b 665 }
666 }
667 }
aef458a0 668 pop(@$x);
669 unshift(@q, $q);
0716bf9b 670 }
671 if (wantarray)
672 {
673 @d = ();
674 if ($dd != 1)
675 {
676 $car = 0;
677 for $xi (reverse @$x)
678 {
61f5c3f5 679 $prd = $car * $MBASE + $xi;
0716bf9b 680 $car = $prd - ($tmp = int($prd / $dd)) * $dd; # see USE_MUL
681 unshift(@d, $tmp);
682 }
683 }
684 else
685 {
686 @d = @$x;
687 }
688 @$x = @q;
61f5c3f5 689 my $d = \@d;
990fb837 690 __strip_zeros($x);
691 __strip_zeros($d);
61f5c3f5 692 return ($x,$d);
0716bf9b 693 }
694 @$x = @q;
990fb837 695 __strip_zeros($x);
61f5c3f5 696 $x;
0716bf9b 697 }
698
ee15d750 699sub _div_use_div
700 {
701 # ref to array, ref to array, modify first array and return remainder if
702 # in list context
ee15d750 703 my ($c,$x,$yorg) = @_;
ee15d750 704
990fb837 705 # the general div algorithmn here is about O(N*N) and thus quite slow, so
706 # we first check for some special cases and use shortcuts to handle them.
707
708 # This works, because we store the numbers in a chunked format where each
709 # element contains 5..7 digits (depending on system).
710
711 # if both numbers have only one element:
61f5c3f5 712 if (@$x == 1 && @$yorg == 1)
713 {
13a12e00 714 # shortcut, $yorg and $x are two small numbers
61f5c3f5 715 if (wantarray)
716 {
717 my $r = [ $x->[0] % $yorg->[0] ];
718 $x->[0] = int($x->[0] / $yorg->[0]);
719 return ($x,$r);
720 }
721 else
722 {
723 $x->[0] = int($x->[0] / $yorg->[0]);
724 return $x;
725 }
726 }
990fb837 727 # if x has more than one, but y has only one element:
28df3e88 728 if (@$yorg == 1)
729 {
730 my $rem;
731 $rem = _mod($c,[ @$x ],$yorg) if wantarray;
732
733 # shortcut, $y is < $BASE
734 my $j = scalar @$x; my $r = 0;
735 my $y = $yorg->[0]; my $b;
736 while ($j-- > 0)
737 {
738 $b = $r * $MBASE + $x->[$j];
739 $x->[$j] = int($b/$y);
740 $r = $b % $y;
741 }
742 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero
743 return ($x,$rem) if wantarray;
744 return $x;
745 }
990fb837 746 # now x and y have more than one element
ee15d750 747
990fb837 748 # check whether y has more elements than x, if yet, the result will be 0
749 if (@$yorg > @$x)
61f5c3f5 750 {
990fb837 751 my $rem;
752 $rem = [@$x] if wantarray; # make copy
753 splice (@$x,1); # keep ref to original array
754 $x->[0] = 0; # set to 0
755 return ($x,$rem) if wantarray; # including remainder?
aef458a0 756 return $x; # only x, which is [0] now
61f5c3f5 757 }
990fb837 758 # check whether the numbers have the same number of elements, in that case
759 # the result will fit into one element and can be computed efficiently
760 if (@$yorg == @$x)
761 {
762 my $rem;
763 # if $yorg has more digits than $x (it's leading element is longer than
764 # the one from $x), the result will also be 0:
765 if (length(int($yorg->[-1])) > length(int($x->[-1])))
766 {
767 $rem = [@$x] if wantarray; # make copy
768 splice (@$x,1); # keep ref to org array
769 $x->[0] = 0; # set to 0
770 return ($x,$rem) if wantarray; # including remainder?
771 return $x;
772 }
773 # now calculate $x / $yorg
091c87b1 774
990fb837 775 if (length(int($yorg->[-1])) == length(int($x->[-1])))
776 {
b68b7ab1 777
778 # We take a shortcut here, because the result must be
779 # between 1 and MAX_VAL (e.g. one element) and rem is not wanted.
780 if (!wantarray)
781 {
da219303 782 # fit's into one Perl scalar, so result can be computed directly
783 # cannot use int() here, because it rounds wrongly on some systems
784 #$x->[0] = int($x->[-1] / $yorg->[-1]);
785 # round to 8 digits, then truncate result to integer
786 $x->[0] = int ( sprintf ("%.8f", $x->[-1] / $yorg->[-1]) );
b68b7ab1 787 splice(@$x,1); # keep single element
788 return $x;
789 }
790
791 # wantarray: return (x,rem)
792 # same length, so make full compare
793
990fb837 794 my $a = 0; my $j = scalar @$x - 1;
795 # manual way (abort if unequal, good for early ne)
796 while ($j >= 0)
797 {
798 last if ($a = $x->[$j] - $yorg->[$j]); $j--;
799 }
aef458a0 800 # $a contains the result of the compare between X and Y
b68b7ab1 801 # a < 0: x < y, a == 0: x == y, a > 0: x > y
990fb837 802 if ($a <= 0)
803 {
b68b7ab1 804 $rem = [ 0 ]; # a = 0 => x == y => rem 0
805 $rem = [@$x] if $a != 0; # a < 0 => x < y => rem = x
aef458a0 806 splice(@$x,1); # keep single element
990fb837 807 $x->[0] = 0; # if $a < 0
b68b7ab1 808 $x->[0] = 1 if $a == 0; # $x == $y
809 return ($x,$rem);
990fb837 810 }
aef458a0 811 # $x >= $y, so proceed normally
b68b7ab1 812
990fb837 813 }
990fb837 814 }
815
816 # all other cases:
817
818 my $y = [ @$yorg ]; # always make copy to preserve
61f5c3f5 819
820 my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);
821
822 $car = $bar = $prd = 0;
823 if (($dd = int($MBASE/($y->[-1]+1))) != 1)
ee15d750 824 {
825 for $xi (@$x)
826 {
827 $xi = $xi * $dd + $car;
61f5c3f5 828 $xi -= ($car = int($xi / $MBASE)) * $MBASE;
ee15d750 829 }
830 push(@$x, $car); $car = 0;
831 for $yi (@$y)
832 {
833 $yi = $yi * $dd + $car;
61f5c3f5 834 $yi -= ($car = int($yi / $MBASE)) * $MBASE;
ee15d750 835 }
836 }
837 else
838 {
839 push(@$x, 0);
840 }
aef458a0 841
842 # @q will accumulate the final result, $q contains the current computed
843 # part of the final result
844
ee15d750 845 @q = (); ($v2,$v1) = @$y[-2,-1];
846 $v2 = 0 unless $v2;
847 while ($#$x > $#$y)
848 {
849 ($u2,$u1,$u0) = @$x[-3..-1];
850 $u2 = 0 unless $u2;
851 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
852 # if $v1 == 0;
aef458a0 853 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$MBASE+$u1)/$v1));
61f5c3f5 854 --$q while ($v2*$q > ($u0*$MBASE+$u1-$q*$v1)*$MBASE+$u2);
ee15d750 855 if ($q)
856 {
857 ($car, $bar) = (0,0);
858 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
859 {
860 $prd = $q * $y->[$yi] + $car;
61f5c3f5 861 $prd -= ($car = int($prd / $MBASE)) * $MBASE;
862 $x->[$xi] += $MBASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0));
ee15d750 863 }
864 if ($x->[-1] < $car + $bar)
865 {
866 $car = 0; --$q;
867 for ($yi = 0, $xi = $#$x-$#$y-1; $yi <= $#$y; ++$yi,++$xi)
868 {
61f5c3f5 869 $x->[$xi] -= $MBASE
aef458a0 870 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $MBASE));
ee15d750 871 }
872 }
873 }
61f5c3f5 874 pop(@$x); unshift(@q, $q);
ee15d750 875 }
876 if (wantarray)
877 {
878 @d = ();
879 if ($dd != 1)
880 {
881 $car = 0;
882 for $xi (reverse @$x)
883 {
61f5c3f5 884 $prd = $car * $MBASE + $xi;
ee15d750 885 $car = $prd - ($tmp = int($prd / $dd)) * $dd;
886 unshift(@d, $tmp);
887 }
888 }
889 else
890 {
891 @d = @$x;
892 }
893 @$x = @q;
61f5c3f5 894 my $d = \@d;
990fb837 895 __strip_zeros($x);
896 __strip_zeros($d);
61f5c3f5 897 return ($x,$d);
ee15d750 898 }
899 @$x = @q;
990fb837 900 __strip_zeros($x);
61f5c3f5 901 $x;
ee15d750 902 }
903
394e6ffb 904##############################################################################
905# testing
906
907sub _acmp
908 {
909 # internal absolute post-normalized compare (ignore signs)
910 # ref to array, ref to array, return <0, 0, >0
911 # arrays must have at least one entry; this is not checked for
394e6ffb 912 my ($c,$cx,$cy) = @_;
091c87b1 913
914 # shortcut for short numbers
915 return (($cx->[0] <=> $cy->[0]) <=> 0)
916 if scalar @$cx == scalar @$cy && scalar @$cx == 1;
394e6ffb 917
f9a08e12 918 # fast comp based on number of array elements (aka pseudo-length)
091c87b1 919 my $lxy = (scalar @$cx - scalar @$cy)
920 # or length of first element if same number of elements (aka difference 0)
921 ||
922 # need int() here because sometimes the last element is '00018' vs '18'
923 (length(int($cx->[-1])) - length(int($cy->[-1])));
394e6ffb 924 return -1 if $lxy < 0; # already differs, ret
925 return 1 if $lxy > 0; # ditto
56d9de68 926
394e6ffb 927 # manual way (abort if unequal, good for early ne)
091c87b1 928 my $a; my $j = scalar @$cx;
929 while (--$j >= 0)
9393ace2 930 {
091c87b1 931 last if ($a = $cx->[$j] - $cy->[$j]);
9393ace2 932 }
091c87b1 933 $a <=> 0;
394e6ffb 934 }
935
936sub _len
937 {
56d9de68 938 # compute number of digits
394e6ffb 939
940 # int() because add/sub sometimes leaves strings (like '00005') instead of
941 # '5' in this place, thus causing length() to report wrong length
942 my $cx = $_[1];
943
56d9de68 944 (@$cx-1)*$BASE_LEN+length(int($cx->[-1]));
394e6ffb 945 }
946
947sub _digit
948 {
949 # return the nth digit, negative values count backward
950 # zero is rightmost, so _digit(123,0) will give 3
951 my ($c,$x,$n) = @_;
952
953 my $len = _len('',$x);
954
955 $n = $len+$n if $n < 0; # -1 last, -2 second-to-last
956 $n = abs($n); # if negative was too big
957 $len--; $n = $len if $n > $len; # n to big?
958
959 my $elem = int($n / $BASE_LEN); # which array element
960 my $digit = $n % $BASE_LEN; # which digit in this element
03874afe 961 $elem = '0000000'.@$x[$elem]; # get element padded with 0's
56d9de68 962 substr($elem,-$digit-1,1);
394e6ffb 963 }
964
965sub _zeros
966 {
967 # return amount of trailing zeros in decimal
968 # check each array elem in _m for having 0 at end as long as elem == 0
969 # Upon finding a elem != 0, stop
970 my $x = $_[1];
9b924220 971
972 return 0 if scalar @$x == 1 && $x->[0] == 0;
973
394e6ffb 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 {
9b924220 995 # return true if arg is zero
996 (((scalar @{$_[1]} == 1) && ($_[1]->[0] == 0))) <=> 0;
394e6ffb 997 }
998
999sub _is_even
1000 {
9b924220 1001 # return true if arg is even
1002 (!($_[1]->[0] & 1)) <=> 0;
394e6ffb 1003 }
1004
1005sub _is_odd
1006 {
9b924220 1007 # return true if arg is even
1008 (($_[1]->[0] & 1)) <=> 0;
394e6ffb 1009 }
1010
1011sub _is_one
1012 {
9b924220 1013 # return true if arg is one
1014 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 1) <=> 0;
1015 }
1016
1017sub _is_two
1018 {
1019 # return true if arg is two
1020 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 2) <=> 0;
1021 }
61f5c3f5 1022
9b924220 1023sub _is_ten
1024 {
1025 # return true if arg is ten
1026 (scalar @{$_[1]} == 1) && ($_[1]->[0] == 10) <=> 0;
394e6ffb 1027 }
1028
1029sub __strip_zeros
1030 {
1031 # internal normalization function that strips leading zeros from the array
1032 # args: ref to array
1033 my $s = shift;
1034
1035 my $cnt = scalar @$s; # get count of parts
1036 my $i = $cnt-1;
1037 push @$s,0 if $i < 0; # div might return empty results, so fix it
1038
61f5c3f5 1039 return $s if @$s == 1; # early out
1040
394e6ffb 1041 #print "strip: cnt $cnt i $i\n";
1042 # '0', '3', '4', '0', '0',
1043 # 0 1 2 3 4
1044 # cnt = 5, i = 4
1045 # i = 4
1046 # i = 3
1047 # => fcnt = cnt - i (5-2 => 3, cnt => 5-1 = 4, throw away from 4th pos)
1048 # >= 1: skip first part (this can be zero)
1049 while ($i > 0) { last if $s->[$i] != 0; $i--; }
1050 $i++; splice @$s,$i if ($i < $cnt); # $i cant be 0
1051 $s;
1052 }
1053
1054###############################################################################
3a427a11 1055# check routine to test internal state for corruptions
394e6ffb 1056
1057sub _check
1058 {
1059 # used by the test suite
1060 my $x = $_[1];
1061
1062 return "$x is not a reference" if !ref($x);
1063
1064 # are all parts are valid?
1065 my $i = 0; my $j = scalar @$x; my ($e,$try);
1066 while ($i < $j)
1067 {
1068 $e = $x->[$i]; $e = 'undef' unless defined $e;
1069 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e)";
1070 last if $e !~ /^[+]?[0-9]+$/;
1071 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (stringify)";
1072 last if "$e" !~ /^[+]?[0-9]+$/;
1073 $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (cat-stringify)";
1074 last if '' . "$e" !~ /^[+]?[0-9]+$/;
1075 $try = ' < 0 || >= $BASE; '."($x, $e)";
1076 last if $e <0 || $e >= $BASE;
1077 # this test is disabled, since new/bnorm and certain ops (like early out
1078 # in add/sub) are allowed/expected to leave '00000' in some elements
1079 #$try = '=~ /^00+/; '."($x, $e)";
1080 #last if $e =~ /^00+/;
1081 $i++;
1082 }
1083 return "Illegal part '$e' at pos $i (tested: $try)" if $i < $j;
3a427a11 1084 0;
394e6ffb 1085 }
1086
1087
1088###############################################################################
394e6ffb 1089
dccbb853 1090sub _mod
1091 {
1092 # if possible, use mod shortcut
1093 my ($c,$x,$yo) = @_;
1094
1095 # slow way since $y to big
1096 if (scalar @$yo > 1)
1097 {
1098 my ($xo,$rem) = _div($c,$x,$yo);
1099 return $rem;
1100 }
aef458a0 1101
dccbb853 1102 my $y = $yo->[0];
027dc388 1103 # both are single element arrays
dccbb853 1104 if (scalar @$x == 1)
1105 {
1106 $x->[0] %= $y;
1107 return $x;
1108 }
1109
aef458a0 1110 # @y is a single element, but @x has more than one element
dccbb853 1111 my $b = $BASE % $y;
1112 if ($b == 0)
1113 {
1114 # when BASE % Y == 0 then (B * BASE) % Y == 0
1115 # (B * BASE) % $y + A % Y => A % Y
1116 # so need to consider only last element: O(1)
1117 $x->[0] %= $y;
1118 }
027dc388 1119 elsif ($b == 1)
1120 {
3a427a11 1121 # else need to go through all elements: O(N), but loop is a bit simplified
027dc388 1122 my $r = 0;
1123 foreach (@$x)
1124 {
28df3e88 1125 $r = ($r + $_) % $y; # not much faster, but heh...
1126 #$r += $_ % $y; $r %= $y;
027dc388 1127 }
1128 $r = 0 if $r == $y;
1129 $x->[0] = $r;
1130 }
dccbb853 1131 else
1132 {
3a427a11 1133 # else need to go through all elements: O(N)
027dc388 1134 my $r = 0; my $bm = 1;
1135 foreach (@$x)
1136 {
28df3e88 1137 $r = ($_ * $bm + $r) % $y;
1138 $bm = ($bm * $b) % $y;
1139
1140 #$r += ($_ % $y) * $bm;
1141 #$bm *= $b;
1142 #$bm %= $y;
1143 #$r %= $y;
027dc388 1144 }
1145 $r = 0 if $r == $y;
1146 $x->[0] = $r;
dccbb853 1147 }
091c87b1 1148 splice (@$x,1); # keep one element of $x
61f5c3f5 1149 $x;
dccbb853 1150 }
1151
0716bf9b 1152##############################################################################
574bacfe 1153# shifts
1154
1155sub _rsft
1156 {
1157 my ($c,$x,$y,$n) = @_;
1158
1159 if ($n != 10)
1160 {
9b924220 1161 $n = _new($c,$n); return _div($c,$x, _pow($c,$n,$y));
61f5c3f5 1162 }
1163
1164 # shortcut (faster) for shifting by 10)
1165 # multiples of $BASE_LEN
1166 my $dst = 0; # destination
1167 my $src = _num($c,$y); # as normal int
56d9de68 1168 my $xlen = (@$x-1)*$BASE_LEN+length(int($x->[-1])); # len of x in digits
90d1b129 1169 if ($src >= $xlen or ($src == $xlen and ! defined $x->[1]))
56d9de68 1170 {
1171 # 12345 67890 shifted right by more than 10 digits => 0
1172 splice (@$x,1); # leave only one element
1173 $x->[0] = 0; # set to zero
1174 return $x;
1175 }
61f5c3f5 1176 my $rem = $src % $BASE_LEN; # remainder to shift
1177 $src = int($src / $BASE_LEN); # source
1178 if ($rem == 0)
1179 {
1180 splice (@$x,0,$src); # even faster, 38.4 => 39.3
574bacfe 1181 }
1182 else
1183 {
61f5c3f5 1184 my $len = scalar @$x - $src; # elems to go
1185 my $vd; my $z = '0'x $BASE_LEN;
1186 $x->[scalar @$x] = 0; # avoid || 0 test inside loop
1187 while ($dst < $len)
574bacfe 1188 {
61f5c3f5 1189 $vd = $z.$x->[$src];
1190 $vd = substr($vd,-$BASE_LEN,$BASE_LEN-$rem);
1191 $src++;
1192 $vd = substr($z.$x->[$src],-$rem,$rem) . $vd;
1193 $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN;
1194 $x->[$dst] = int($vd);
1195 $dst++;
574bacfe 1196 }
61f5c3f5 1197 splice (@$x,$dst) if $dst > 0; # kill left-over array elems
56b9c951 1198 pop @$x if $x->[-1] == 0 && @$x > 1; # kill last element if 0
61f5c3f5 1199 } # else rem == 0
574bacfe 1200 $x;
1201 }
1202
1203sub _lsft
1204 {
1205 my ($c,$x,$y,$n) = @_;
1206
1207 if ($n != 10)
1208 {
9b924220 1209 $n = _new($c,$n); return _mul($c,$x, _pow($c,$n,$y));
574bacfe 1210 }
61f5c3f5 1211
1212 # shortcut (faster) for shifting by 10) since we are in base 10eX
1213 # multiples of $BASE_LEN:
1214 my $src = scalar @$x; # source
1215 my $len = _num($c,$y); # shift-len as normal int
1216 my $rem = $len % $BASE_LEN; # remainder to shift
1217 my $dst = $src + int($len/$BASE_LEN); # destination
1218 my $vd; # further speedup
1219 $x->[$src] = 0; # avoid first ||0 for speed
1220 my $z = '0' x $BASE_LEN;
1221 while ($src >= 0)
574bacfe 1222 {
61f5c3f5 1223 $vd = $x->[$src]; $vd = $z.$vd;
1224 $vd = substr($vd,-$BASE_LEN+$rem,$BASE_LEN-$rem);
1225 $vd .= $src > 0 ? substr($z.$x->[$src-1],-$BASE_LEN,$rem) : '0' x $rem;
1226 $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN;
1227 $x->[$dst] = int($vd);
1228 $dst--; $src--;
574bacfe 1229 }
61f5c3f5 1230 # set lowest parts to 0
1231 while ($dst >= 0) { $x->[$dst--] = 0; }
1232 # fix spurios last zero element
1233 splice @$x,-1 if $x->[-1] == 0;
574bacfe 1234 $x;
1235 }
1236
027dc388 1237sub _pow
1238 {
1239 # power of $x to $y
1240 # ref to array, ref to array, return ref to array
1241 my ($c,$cx,$cy) = @_;
1242
b282a552 1243 if (scalar @$cy == 1 && $cy->[0] == 0)
1244 {
1245 splice (@$cx,1); $cx->[0] = 1; # y == 0 => x => 1
1246 return $cx;
1247 }
1248 if ((scalar @$cx == 1 && $cx->[0] == 1) || # x == 1
1249 (scalar @$cy == 1 && $cy->[0] == 1)) # or y == 1
1250 {
1251 return $cx;
1252 }
1253 if (scalar @$cx == 1 && $cx->[0] == 0)
1254 {
1255 splice (@$cx,1); $cx->[0] = 0; # 0 ** y => 0 (if not y <= 0)
1256 return $cx;
1257 }
1258
027dc388 1259 my $pow2 = _one();
1ddff52a 1260
9b924220 1261 my $y_bin = _as_bin($c,$cy); $y_bin =~ s/^0b//;
1ddff52a 1262 my $len = length($y_bin);
1263 while (--$len > 0)
027dc388 1264 {
1ddff52a 1265 _mul($c,$pow2,$cx) if substr($y_bin,$len,1) eq '1'; # is odd?
027dc388 1266 _mul($c,$cx,$cx);
1267 }
1ddff52a 1268
1269 _mul($c,$cx,$pow2);
61f5c3f5 1270 $cx;
027dc388 1271 }
1272
b3abae2a 1273sub _fac
1274 {
1275 # factorial of $x
1276 # ref to array, return ref to array
1277 my ($c,$cx) = @_;
1278
1279 if ((@$cx == 1) && ($cx->[0] <= 2))
1280 {
091c87b1 1281 $cx->[0] ||= 1; # 0 => 1, 1 => 1, 2 => 2
b3abae2a 1282 return $cx;
1283 }
1284
1285 # go forward until $base is exceeded
091c87b1 1286 # limit is either $x steps (steps == 100 means a result always too high) or
1287 # $base.
b3abae2a 1288 my $steps = 100; $steps = $cx->[0] if @$cx == 1;
091c87b1 1289 my $r = 2; my $cf = 3; my $step = 2; my $last = $r;
1290 while ($r*$cf < $BASE && $step < $steps)
b3abae2a 1291 {
1292 $last = $r; $r *= $cf++; $step++;
1293 }
091c87b1 1294 if ((@$cx == 1) && $step == $cx->[0])
b3abae2a 1295 {
091c87b1 1296 # completely done, so keep reference to $x and return
1297 $cx->[0] = $r;
b3abae2a 1298 return $cx;
1299 }
091c87b1 1300
990fb837 1301 # now we must do the left over steps
091c87b1 1302 my $n; # steps still to do
1303 if (scalar @$cx == 1)
1304 {
1305 $n = $cx->[0];
1306 }
1307 else
1308 {
1309 $n = _copy($c,$cx);
1310 }
b3abae2a 1311
091c87b1 1312 $cx->[0] = $last; splice (@$cx,1); # keep ref to $x
990fb837 1313 my $zero_elements = 0;
091c87b1 1314
1315 # do left-over steps fit into a scalar?
1316 if (ref $n eq 'ARRAY')
b3abae2a 1317 {
091c87b1 1318 # No, so use slower inc() & cmp()
1319 $step = [$step];
1320 while (_acmp($step,$n) <= 0)
990fb837 1321 {
091c87b1 1322 # as soon as the last element of $cx is 0, we split it up and remember
1323 # how many zeors we got so far. The reason is that n! will accumulate
1324 # zeros at the end rather fast.
990fb837 1325 if ($cx->[0] == 0)
1326 {
1327 $zero_elements ++; shift @$cx;
1328 }
091c87b1 1329 _mul($c,$cx,$step); _inc($c,$step);
990fb837 1330 }
990fb837 1331 }
091c87b1 1332 else
990fb837 1333 {
091c87b1 1334 # Yes, so we can speed it up slightly
1335 while ($step <= $n)
990fb837 1336 {
091c87b1 1337 # When the last element of $cx is 0, we split it up and remember
1338 # how many we got so far. The reason is that n! will accumulate
1339 # zeros at the end rather fast.
1340 if ($cx->[0] == 0)
1341 {
1342 $zero_elements ++; shift @$cx;
1343 }
1344 _mul($c,$cx,[$step]); $step++;
990fb837 1345 }
990fb837 1346 }
1347 # multiply in the zeros again
1348 while ($zero_elements-- > 0)
1349 {
1350 unshift @$cx, 0;
b3abae2a 1351 }
091c87b1 1352 $cx; # return result
1353 }
1354
9b924220 1355#############################################################################
1356
091c87b1 1357sub _log_int
1358 {
1359 # calculate integer log of $x to base $base
1360 # ref to array, ref to array - return ref to array
1361 my ($c,$x,$base) = @_;
1362
1363 # X == 0 => NaN
1364 return if (scalar @$x == 1 && $x->[0] == 0);
1365 # BASE 0 or 1 => NaN
1366 return if (scalar @$base == 1 && $base->[0] < 2);
b282a552 1367 my $cmp = _acmp($c,$x,$base); # X == BASE => 1
091c87b1 1368 if ($cmp == 0)
1369 {
1370 splice (@$x,1); $x->[0] = 1;
8df1e0a2 1371 return ($x,1)
091c87b1 1372 }
1373 # X < BASE
1374 if ($cmp < 0)
1375 {
1376 splice (@$x,1); $x->[0] = 0;
8df1e0a2 1377 return ($x,undef);
091c87b1 1378 }
1379
1380 # this trial multiplication is very fast, even for large counts (like for
1381 # 2 ** 1024, since this still requires only 1024 very fast steps
1382 # (multiplication of a large number by a very small number is very fast))
1383 my $x_org = _copy($c,$x); # preserve x
8df1e0a2 1384 splice(@$x,1); $x->[0] = 1; # keep ref to $x
1385
b282a552 1386 my $trial = _copy($c,$base);
1387
1388 # XXX TODO this only works if $base has only one element
1389 if (scalar @$base == 1)
1390 {
1391 # compute int ( length_in_base_10(X) / ( log(base) / log(10) ) )
1392 my $len = _len($c,$x_org);
1393 my $res = int($len / (log($base->[0]) / log(10))) || 1; # avoid $res == 0
1394
1395 $x->[0] = $res;
1396 $trial = _pow ($c, _copy($c, $base), $x);
1397 my $a = _acmp($x,$trial,$x_org);
1398 return ($x,1) if $a == 0;
3a427a11 1399 # we now know that $res is too small
b282a552 1400 if ($res < 0)
1401 {
1402 _mul($c,$trial,$base); _add($c, $x, [1]);
1403 }
1404 else
1405 {
1406 # or too big
1407 _div($c,$trial,$base); _sub($c, $x, [1]);
1408 }
1409 # did we now get the right result?
1410 $a = _acmp($x,$trial,$x_org);
1411 return ($x,1) if $a == 0; # yes, exactly
1412 # still too big
1413 if ($a > 0)
1414 {
1415 _div($c,$trial,$base); _sub($c, $x, [1]);
1416 }
1417 }
1418
8df1e0a2 1419 # simple loop that increments $x by two in each step, possible overstepping
1420 # the real result by one
091c87b1 1421
b282a552 1422 my $a;
8df1e0a2 1423 my $base_mul = _mul($c, _copy($c,$base), $base);
1424
9b924220 1425 while (($a = _acmp($c,$trial,$x_org)) < 0)
091c87b1 1426 {
8df1e0a2 1427 _mul($c,$trial,$base_mul); _add($c, $x, [2]);
091c87b1 1428 }
8df1e0a2 1429
1430 my $exact = 1;
1431 if ($a > 0)
091c87b1 1432 {
8df1e0a2 1433 # overstepped the result
1434 _dec($c, $x);
1435 _div($c,$trial,$base);
9b924220 1436 $a = _acmp($c,$trial,$x_org);
8df1e0a2 1437 if ($a > 0)
091c87b1 1438 {
8df1e0a2 1439 _dec($c, $x);
091c87b1 1440 }
8df1e0a2 1441 $exact = 0 if $a != 0;
091c87b1 1442 }
1443
8df1e0a2 1444 ($x,$exact); # return result
b3abae2a 1445 }
1446
56d9de68 1447# for debugging:
1448 use constant DEBUG => 0;
1449 my $steps = 0;
1450 sub steps { $steps };
b3abae2a 1451
1452sub _sqrt
0716bf9b 1453 {
56d9de68 1454 # square-root of $x in place
990fb837 1455 # Compute a guess of the result (by rule of thumb), then improve it via
56d9de68 1456 # Newton's method.
394e6ffb 1457 my ($c,$x) = @_;
0716bf9b 1458
394e6ffb 1459 if (scalar @$x == 1)
1460 {
56d9de68 1461 # fit's into one Perl scalar, so result can be computed directly
394e6ffb 1462 $x->[0] = int(sqrt($x->[0]));
1463 return $x;
1464 }
1465 my $y = _copy($c,$x);
b3abae2a 1466 # hopefully _len/2 is < $BASE, the -1 is to always undershot the guess
1467 # since our guess will "grow"
1468 my $l = int((_len($c,$x)-1) / 2);
1469
56d9de68 1470 my $lastelem = $x->[-1]; # for guess
b3abae2a 1471 my $elems = scalar @$x - 1;
1472 # not enough digits, but could have more?
56d9de68 1473 if ((length($lastelem) <= 3) && ($elems > 1))
b3abae2a 1474 {
1475 # right-align with zero pad
1476 my $len = length($lastelem) & 1;
1477 print "$lastelem => " if DEBUG;
1478 $lastelem .= substr($x->[-2] . '0' x $BASE_LEN,0,$BASE_LEN);
1479 # former odd => make odd again, or former even to even again
56d9de68 1480 $lastelem = $lastelem / 10 if (length($lastelem) & 1) != $len;
b3abae2a 1481 print "$lastelem\n" if DEBUG;
1482 }
0716bf9b 1483
61f5c3f5 1484 # construct $x (instead of _lsft($c,$x,$l,10)
1485 my $r = $l % $BASE_LEN; # 10000 00000 00000 00000 ($BASE_LEN=5)
1486 $l = int($l / $BASE_LEN);
b3abae2a 1487 print "l = $l " if DEBUG;
56d9de68 1488
1489 splice @$x,$l; # keep ref($x), but modify it
1490
b3abae2a 1491 # we make the first part of the guess not '1000...0' but int(sqrt($lastelem))
1492 # that gives us:
56d9de68 1493 # 14400 00000 => sqrt(14400) => guess first digits to be 120
1494 # 144000 000000 => sqrt(144000) => guess 379
b3abae2a 1495
b3abae2a 1496 print "$lastelem (elems $elems) => " if DEBUG;
1497 $lastelem = $lastelem / 10 if ($elems & 1 == 1); # odd or even?
1498 my $g = sqrt($lastelem); $g =~ s/\.//; # 2.345 => 2345
1499 $r -= 1 if $elems & 1 == 0; # 70 => 7
1500
1501 # padd with zeros if result is too short
1502 $x->[$l--] = int(substr($g . '0' x $r,0,$r+1));
1503 print "now ",$x->[-1] if DEBUG;
1504 print " would have been ", int('1' . '0' x $r),"\n" if DEBUG;
56d9de68 1505
b3abae2a 1506 # If @$x > 1, we could compute the second elem of the guess, too, to create
56d9de68 1507 # an even better guess. Not implemented yet. Does it improve performance?
b3abae2a 1508 $x->[$l--] = 0 while ($l >= 0); # all other digits of guess are zero
56d9de68 1509
9b924220 1510 print "start x= ",_str($c,$x),"\n" if DEBUG;
394e6ffb 1511 my $two = _two();
1512 my $last = _zero();
1513 my $lastlast = _zero();
b3abae2a 1514 $steps = 0 if DEBUG;
394e6ffb 1515 while (_acmp($c,$last,$x) != 0 && _acmp($c,$lastlast,$x) != 0)
1516 {
b3abae2a 1517 $steps++ if DEBUG;
394e6ffb 1518 $lastlast = _copy($c,$last);
1519 $last = _copy($c,$x);
1520 _add($c,$x, _div($c,_copy($c,$y),$x));
1521 _div($c,$x, $two );
9b924220 1522 print " x= ",_str($c,$x),"\n" if DEBUG;
394e6ffb 1523 }
b3abae2a 1524 print "\nsteps in sqrt: $steps, " if DEBUG;
394e6ffb 1525 _dec($c,$x) if _acmp($c,$y,_mul($c,_copy($c,$x),$x)) < 0; # overshot?
b3abae2a 1526 print " final ",$x->[-1],"\n" if DEBUG;
394e6ffb 1527 $x;
0716bf9b 1528 }
1529
990fb837 1530sub _root
1531 {
1532 # take n'th root of $x in place (n >= 3)
990fb837 1533 my ($c,$x,$n) = @_;
1534
1535 if (scalar @$x == 1)
1536 {
1537 if (scalar @$n > 1)
1538 {
1539 # result will always be smaller than 2 so trunc to 1 at once
1540 $x->[0] = 1;
1541 }
1542 else
1543 {
1544 # fit's into one Perl scalar, so result can be computed directly
091c87b1 1545 # cannot use int() here, because it rounds wrongly (try
1546 # (81 ** 3) ** (1/3) to see what I mean)
1547 #$x->[0] = int( $x->[0] ** (1 / $n->[0]) );
1548 # round to 8 digits, then truncate result to integer
1549 $x->[0] = int ( sprintf ("%.8f", $x->[0] ** (1 / $n->[0]) ) );
990fb837 1550 }
1551 return $x;
1552 }
1553
3a427a11 1554 # we know now that X is more than one element long
1555
c38b2de2 1556 # if $n is a power of two, we can repeatedly take sqrt($X) and find the
1557 # proper result, because sqrt(sqrt($x)) == root($x,4)
1558 my $b = _as_bin($c,$n);
9b924220 1559 if ($b =~ /0b1(0+)$/)
c38b2de2 1560 {
1561 my $count = CORE::length($1); # 0b100 => len('00') => 2
1562 my $cnt = $count; # counter for loop
1563 unshift (@$x, 0); # add one element, together with one
1564 # more below in the loop this makes 2
1565 while ($cnt-- > 0)
1566 {
1567 # 'inflate' $X by adding one element, basically computing
1568 # $x * $BASE * $BASE. This gives us more $BASE_LEN digits for result
1569 # since len(sqrt($X)) approx == len($x) / 2.
1570 unshift (@$x, 0);
1571 # calculate sqrt($x), $x is now one element to big, again. In the next
1572 # round we make that two, again.
1573 _sqrt($c,$x);
1574 }
1575 # $x is now one element to big, so truncate result by removing it
1576 splice (@$x,0,1);
1577 }
1578 else
1579 {
091c87b1 1580 # trial computation by starting with 2,4,8,16 etc until we overstep
3a427a11 1581 my $step;
091c87b1 1582 my $trial = _two();
1583
3a427a11 1584 # while still to do more than X steps
1585 do
091c87b1 1586 {
3a427a11 1587 $step = _two();
1588 while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0)
1589 {
1590 _mul ($c, $step, [2]);
1591 _add ($c, $trial, $step);
1592 }
1593
1594 # hit exactly?
1595 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) == 0)
1596 {
1597 @$x = @$trial; # make copy while preserving ref to $x
1598 return $x;
1599 }
1600 # overstepped, so go back on step
1601 _sub($c, $trial, $step);
1602 } while (scalar @$step > 1 || $step->[0] > 128);
091c87b1 1603
3a427a11 1604 # reset step to 2
1605 $step = _two();
091c87b1 1606 # add two, because $trial cannot be exactly the result (otherwise we would
1607 # alrady have found it)
1608 _add($c, $trial, $step);
1609
3a427a11 1610 # and now add more and more (2,4,6,8,10 etc)
1611 while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0)
1612 {
1613 _add ($c, $trial, $step);
1614 }
091c87b1 1615
1616 # hit not exactly? (overstepped)
091c87b1 1617 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0)
1618 {
1619 _dec($c,$trial);
1620 }
3a427a11 1621
1622 # hit not exactly? (overstepped)
1623 # 80 too small, 81 slightly too big, 82 too big
091c87b1 1624 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0)
1625 {
3a427a11 1626 _dec ($c, $trial);
091c87b1 1627 }
3a427a11 1628
091c87b1 1629 @$x = @$trial; # make copy while preserving ref to $x
1630 return $x;
c38b2de2 1631 }
990fb837 1632 $x;
1633 }
1634
394e6ffb 1635##############################################################################
1636# binary stuff
0716bf9b 1637
394e6ffb 1638sub _and
1639 {
1640 my ($c,$x,$y) = @_;
0716bf9b 1641
394e6ffb 1642 # the shortcut makes equal, large numbers _really_ fast, and makes only a
1643 # very small performance drop for small numbers (e.g. something with less
1644 # than 32 bit) Since we optimize for large numbers, this is enabled.
1645 return $x if _acmp($c,$x,$y) == 0; # shortcut
0716bf9b 1646
394e6ffb 1647 my $m = _one(); my ($xr,$yr);
1648 my $mask = $AND_MASK;
1649
1650 my $x1 = $x;
1651 my $y1 = _copy($c,$y); # make copy
1652 $x = _zero();
1653 my ($b,$xrr,$yrr);
1654 use integer;
1655 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
1656 {
1657 ($x1, $xr) = _div($c,$x1,$mask);
1658 ($y1, $yr) = _div($c,$y1,$mask);
1659
1660 # make ints() from $xr, $yr
9b924220 1661 # this is when the AND_BITS are greater than $BASE and is slower for
394e6ffb 1662 # small (<256 bits) numbers, but faster for large numbers. Disabled
1663 # due to KISS principle
1664
1665# $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1666# $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
9b924220 1667# _add($c,$x, _mul($c, _new( $c, ($xrr & $yrr) ), $m) );
394e6ffb 1668
61f5c3f5 1669 # 0+ due to '&' doesn't work in strings
1670 _add($c,$x, _mul($c, [ 0+$xr->[0] & 0+$yr->[0] ], $m) );
394e6ffb 1671 _mul($c,$m,$mask);
1672 }
1673 $x;
0716bf9b 1674 }
1675
394e6ffb 1676sub _xor
0716bf9b 1677 {
394e6ffb 1678 my ($c,$x,$y) = @_;
1679
1680 return _zero() if _acmp($c,$x,$y) == 0; # shortcut (see -and)
1681
1682 my $m = _one(); my ($xr,$yr);
1683 my $mask = $XOR_MASK;
1684
1685 my $x1 = $x;
1686 my $y1 = _copy($c,$y); # make copy
1687 $x = _zero();
1688 my ($b,$xrr,$yrr);
1689 use integer;
1690 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
0716bf9b 1691 {
394e6ffb 1692 ($x1, $xr) = _div($c,$x1,$mask);
1693 ($y1, $yr) = _div($c,$y1,$mask);
1694 # make ints() from $xr, $yr (see _and())
1695 #$b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1696 #$b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
9b924220 1697 #_add($c,$x, _mul($c, _new( $c, ($xrr ^ $yrr) ), $m) );
61f5c3f5 1698
1699 # 0+ due to '^' doesn't work in strings
1700 _add($c,$x, _mul($c, [ 0+$xr->[0] ^ 0+$yr->[0] ], $m) );
394e6ffb 1701 _mul($c,$m,$mask);
0716bf9b 1702 }
394e6ffb 1703 # the loop stops when the shorter of the two numbers is exhausted
1704 # the remainder of the longer one will survive bit-by-bit, so we simple
1705 # multiply-add it in
1706 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1);
1707 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1);
1708
1709 $x;
0716bf9b 1710 }
1711
394e6ffb 1712sub _or
0716bf9b 1713 {
394e6ffb 1714 my ($c,$x,$y) = @_;
0716bf9b 1715
394e6ffb 1716 return $x if _acmp($c,$x,$y) == 0; # shortcut (see _and)
0716bf9b 1717
394e6ffb 1718 my $m = _one(); my ($xr,$yr);
1719 my $mask = $OR_MASK;
0716bf9b 1720
394e6ffb 1721 my $x1 = $x;
1722 my $y1 = _copy($c,$y); # make copy
1723 $x = _zero();
1724 my ($b,$xrr,$yrr);
1725 use integer;
1726 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
1727 {
1728 ($x1, $xr) = _div($c,$x1,$mask);
1729 ($y1, $yr) = _div($c,$y1,$mask);
1730 # make ints() from $xr, $yr (see _and())
1731# $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1732# $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
9b924220 1733# _add($c,$x, _mul($c, _new( $c, ($xrr | $yrr) ), $m) );
394e6ffb 1734
61f5c3f5 1735 # 0+ due to '|' doesn't work in strings
1736 _add($c,$x, _mul($c, [ 0+$xr->[0] | 0+$yr->[0] ], $m) );
394e6ffb 1737 _mul($c,$m,$mask);
1738 }
1739 # the loop stops when the shorter of the two numbers is exhausted
1740 # the remainder of the longer one will survive bit-by-bit, so we simple
1741 # multiply-add it in
1742 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1);
1743 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1);
1744
1745 $x;
0716bf9b 1746 }
1747
61f5c3f5 1748sub _as_hex
1749 {
1750 # convert a decimal number to hex (ref to array, return ref to string)
1751 my ($c,$x) = @_;
1752
091c87b1 1753 # fit's into one element (handle also 0x0 case)
03874afe 1754 return sprintf("0x%x",$x->[0]) if @$x == 1;
990fb837 1755
61f5c3f5 1756 my $x1 = _copy($c,$x);
1757
1758 my $es = '';
1ddff52a 1759 my ($xr, $h, $x10000);
1760 if ($] >= 5.006)
1761 {
1762 $x10000 = [ 0x10000 ]; $h = 'h4';
1763 }
1764 else
1765 {
1766 $x10000 = [ 0x1000 ]; $h = 'h3';
1767 }
091c87b1 1768 while (@$x1 != 1 || $x1->[0] != 0) # _is_zero()
61f5c3f5 1769 {
1770 ($x1, $xr) = _div($c,$x1,$x10000);
091c87b1 1771 $es .= unpack($h,pack('v',$xr->[0])); # XXX TODO: why pack('v',...)?
61f5c3f5 1772 }
1773 $es = reverse $es;
1774 $es =~ s/^[0]+//; # strip leading zeros
03874afe 1775 '0x' . $es; # return result prepended with 0x
61f5c3f5 1776 }
1777
1778sub _as_bin
1779 {
1780 # convert a decimal number to bin (ref to array, return ref to string)
1781 my ($c,$x) = @_;
1782
091c87b1 1783 # fit's into one element (and Perl recent enough), handle also 0b0 case
1784 # handle zero case for older Perls
1785 if ($] <= 5.005 && @$x == 1 && $x->[0] == 0)
1786 {
9b924220 1787 my $t = '0b0'; return $t;
091c87b1 1788 }
1789 if (@$x == 1 && $] >= 5.006)
990fb837 1790 {
091c87b1 1791 my $t = sprintf("0b%b",$x->[0]);
9b924220 1792 return $t;
990fb837 1793 }
61f5c3f5 1794 my $x1 = _copy($c,$x);
1795
1796 my $es = '';
1ddff52a 1797 my ($xr, $b, $x10000);
1798 if ($] >= 5.006)
1799 {
1800 $x10000 = [ 0x10000 ]; $b = 'b16';
1801 }
1802 else
1803 {
1804 $x10000 = [ 0x1000 ]; $b = 'b12';
1805 }
091c87b1 1806 while (!(@$x1 == 1 && $x1->[0] == 0)) # _is_zero()
61f5c3f5 1807 {
1808 ($x1, $xr) = _div($c,$x1,$x10000);
091c87b1 1809 $es .= unpack($b,pack('v',$xr->[0])); # XXX TODO: why pack('v',...)?
1810 # $es .= unpack($b,$xr->[0]);
61f5c3f5 1811 }
1812 $es = reverse $es;
1813 $es =~ s/^[0]+//; # strip leading zeros
03874afe 1814 '0b' . $es; # return result prepended with 0b
61f5c3f5 1815 }
1816
394e6ffb 1817sub _from_hex
0716bf9b 1818 {
394e6ffb 1819 # convert a hex number to decimal (ref to string, return ref to array)
1820 my ($c,$hs) = @_;
0716bf9b 1821
2d2b2744 1822 my $m = _new($c, 0x10000000); # 28 bit at a time (<32 bit!)
03874afe 1823 my $d = 7; # 7 digits at a time
1824 if ($] <= 5.006)
1825 {
1826 # for older Perls, play safe
1827 $m = [ 0x10000 ]; # 16 bit at a time (<32 bit!)
1828 $d = 4; # 4 digits at a time
1829 }
1830
394e6ffb 1831 my $mul = _one();
394e6ffb 1832 my $x = _zero();
0716bf9b 1833
03874afe 1834 my $len = int( (length($hs)-2)/$d ); # $d digit parts, w/o the '0x'
1835 my $val; my $i = -$d;
394e6ffb 1836 while ($len >= 0)
1837 {
03874afe 1838 $val = substr($hs,$i,$d); # get hex digits
394e6ffb 1839 $val =~ s/^[+-]?0x// if $len == 0; # for last part only because
1840 $val = hex($val); # hex does not like wrong chars
03874afe 1841 $i -= $d; $len --;
2d2b2744 1842 my $adder = [ $val ];
1843 # if the resulting number was to big to fit into one element, create a
1844 # two-element version (bug found by Mark Lakata - Thanx!)
1845 if (CORE::length($val) > $BASE_LEN)
1846 {
1847 $adder = _new($c,$val);
1848 }
1849 _add ($c, $x, _mul ($c, $adder, $mul ) ) if $val != 0;
394e6ffb 1850 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul
1851 }
1852 $x;
1853 }
1854
1855sub _from_bin
0716bf9b 1856 {
394e6ffb 1857 # convert a hex number to decimal (ref to string, return ref to array)
1858 my ($c,$bs) = @_;
0716bf9b 1859
091c87b1 1860 # instead of converting X (8) bit at a time, it is faster to "convert" the
13a12e00 1861 # number to hex, and then call _from_hex.
1862
9b924220 1863 my $hs = $bs;
13a12e00 1864 $hs =~ s/^[+-]?0b//; # remove sign and 0b
1865 my $l = length($hs); # bits
1866 $hs = '0' x (8-($l % 8)) . $hs if ($l % 8) != 0; # padd left side w/ 0
03874afe 1867 my $h = '0x' . unpack('H*', pack ('B*', $hs)); # repack as hex
091c87b1 1868
03874afe 1869 $c->_from_hex($h);
0716bf9b 1870 }
1871
07d34614 1872##############################################################################
1873# special modulus functions
1874
56d9de68 1875sub _modinv
d614cd8b 1876 {
56d9de68 1877 # modular inverse
1878 my ($c,$x,$y) = @_;
1ddff52a 1879
56d9de68 1880 my $u = _zero($c); my $u1 = _one($c);
1881 my $a = _copy($c,$y); my $b = _copy($c,$x);
1ddff52a 1882
1883 # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the
56d9de68 1884 # result ($u) at the same time. See comments in BigInt for why this works.
1885 my $q;
1886 ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1
1887 my $sign = 1;
1ddff52a 1888 while (!_is_zero($c,$b))
1889 {
56d9de68 1890 my $t = _add($c, # step 2:
1891 _mul($c,_copy($c,$u1), $q) , # t = u1 * q
1892 $u ); # + u
1893 $u = $u1; # u = u1, u1 = t
1894 $u1 = $t;
1895 $sign = -$sign;
1896 ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1
1ddff52a 1897 }
1898
1899 # if the gcd is not 1, then return NaN
56d9de68 1900 return (undef,undef) unless _is_one($c,$a);
1901
03874afe 1902 ($u1, $sign == 1 ? '+' : '-');
d614cd8b 1903 }
1904
1905sub _modpow
1906 {
1907 # modulus of power ($x ** $y) % $z
07d34614 1908 my ($c,$num,$exp,$mod) = @_;
1909
1910 # in the trivial case,
1911 if (_is_one($c,$mod))
1912 {
1913 splice @$num,0,1; $num->[0] = 0;
1914 return $num;
1915 }
1916 if ((scalar @$num == 1) && (($num->[0] == 0) || ($num->[0] == 1)))
1917 {
1918 $num->[0] = 1;
1919 return $num;
1920 }
1ddff52a 1921
1922# $num = _mod($c,$num,$mod); # this does not make it faster
07d34614 1923
1924 my $acc = _copy($c,$num); my $t = _one();
1925
9b924220 1926 my $expbin = _as_bin($c,$exp); $expbin =~ s/^0b//;
1ddff52a 1927 my $len = length($expbin);
1928 while (--$len >= 0)
07d34614 1929 {
1ddff52a 1930 if ( substr($expbin,$len,1) eq '1') # is_odd
07d34614 1931 {
1932 _mul($c,$t,$acc);
1933 $t = _mod($c,$t,$mod);
1934 }
1935 _mul($c,$acc,$acc);
1936 $acc = _mod($c,$acc,$mod);
07d34614 1937 }
1938 @$num = @$t;
1939 $num;
d614cd8b 1940 }
1941
9b924220 1942sub _gcd
1943 {
1944 # greatest common divisor
1945 my ($c,$x,$y) = @_;
1946
b68b7ab1 1947 while ( (scalar @$y != 1) || ($y->[0] != 0) ) # while ($y != 0)
9b924220 1948 {
1949 my $t = _copy($c,$y);
1950 $y = _mod($c, $x, $y);
1951 $x = $t;
1952 }
1953 $x;
1954 }
1955
394e6ffb 1956##############################################################################
1957##############################################################################
1958
0716bf9b 19591;
1960__END__
1961
1962=head1 NAME
1963
1964Math::BigInt::Calc - Pure Perl module to support Math::BigInt
1965
1966=head1 SYNOPSIS
1967
ee15d750 1968Provides support for big integer calculations. Not intended to be used by other
091c87b1 1969modules. Other modules which sport the same functions can also be used to support
1970Math::BigInt, like Math::BigInt::GMP or Math::BigInt::Pari.
0716bf9b 1971
1972=head1 DESCRIPTION
1973
027dc388 1974In order to allow for multiple big integer libraries, Math::BigInt was
1975rewritten to use library modules for core math routines. Any module which
1976follows the same API as this can be used instead by using the following:
0716bf9b 1977
ee15d750 1978 use Math::BigInt lib => 'libname';
0716bf9b 1979
027dc388 1980'libname' is either the long name ('Math::BigInt::Pari'), or only the short
1981version like 'Pari'.
1982
990fb837 1983=head1 STORAGE
1984
1985=head1 METHODS
0716bf9b 1986
027dc388 1987The following functions MUST be defined in order to support the use by
9b924220 1988Math::BigInt v1.70 or later:
0716bf9b 1989
9b924220 1990 api_version() return API version, minimum 1 for v1.70
0716bf9b 1991 _new(string) return ref to new object from ref to decimal string
1992 _zero() return a new object with value 0
1993 _one() return a new object with value 1
9b924220 1994 _two() return a new object with value 2
1995 _ten() return a new object with value 10
0716bf9b 1996
1997 _str(obj) return ref to a string representing the object
1998 _num(obj) returns a Perl integer/floating point number
1999 NOTE: because of Perl numeric notation defaults,
2000 the _num'ified obj may lose accuracy due to
2001 machine-dependend floating point size limitations
2002
2003 _add(obj,obj) Simple addition of two objects
2004 _mul(obj,obj) Multiplication of two objects
2005 _div(obj,obj) Division of the 1st object by the 2nd
b22b3e31 2006 In list context, returns (result,remainder).
2007 NOTE: this is integer math, so no
2008 fractional part will be returned.
990fb837 2009 The second operand will be not be 0, so no need to
2010 check for that.
b22b3e31 2011 _sub(obj,obj) Simple subtraction of 1 object from another
0716bf9b 2012 a third, optional parameter indicates that the params
2013 are swapped. In this case, the first param needs to
2014 be preserved, while you can destroy the second.
2015 sub (x,y,1) => return x - y and keep x intact!
e745a66c 2016 _dec(obj) decrement object by one (input is garant. to be > 0)
2017 _inc(obj) increment object by one
2018
0716bf9b 2019
2020 _acmp(obj,obj) <=> operator for objects (return -1, 0 or 1)
2021
2022 _len(obj) returns count of the decimal digits of the object
2023 _digit(obj,n) returns the n'th decimal digit of object
2024
9b924220 2025 _is_one(obj) return true if argument is 1
2026 _is_two(obj) return true if argument is 2
2027 _is_ten(obj) return true if argument is 10
0716bf9b 2028 _is_zero(obj) return true if argument is 0
2029 _is_even(obj) return true if argument is even (0,2,4,6..)
2030 _is_odd(obj) return true if argument is odd (1,3,5,7..)
2031
2032 _copy return a ref to a true copy of the object
2033
2034 _check(obj) check whether internal representation is still intact
2035 return 0 for ok, otherwise error message as string
2036
0716bf9b 2037 _from_hex(str) return ref to new object from ref to hexadecimal string
2038 _from_bin(str) return ref to new object from ref to binary string
2039
9b924220 2040 _as_hex(str) return string containing the value as
ee15d750 2041 unsigned hex string, with the '0x' prepended.
2042 Leading zeros must be stripped.
2043 _as_bin(str) Like as_hex, only as binary string containing only
2044 zeros and ones. Leading zeros must be stripped and a
2045 '0b' must be prepended.
2046
0716bf9b 2047 _rsft(obj,N,B) shift object in base B by N 'digits' right
2048 _lsft(obj,N,B) shift object in base B by N 'digits' left
2049
2050 _xor(obj1,obj2) XOR (bit-wise) object 1 with object 2
dccbb853 2051 Note: XOR, AND and OR pad with zeros if size mismatches
0716bf9b 2052 _and(obj1,obj2) AND (bit-wise) object 1 with object 2
2053 _or(obj1,obj2) OR (bit-wise) object 1 with object 2
2054
dccbb853 2055 _mod(obj,obj) Return remainder of div of the 1st by the 2nd object
990fb837 2056 _sqrt(obj) return the square root of object (truncated to int)
2057 _root(obj) return the n'th (n >= 3) root of obj (truncated to int)
b3abae2a 2058 _fac(obj) return factorial of object 1 (1*2*3*4..)
0716bf9b 2059 _pow(obj,obj) return object 1 to the power of object 2
b282a552 2060 return undef for NaN
b22b3e31 2061 _zeros(obj) return number of trailing decimal zeros
d614cd8b 2062 _modinv return inverse modulus
2063 _modpow return modulus of power ($x ** $y) % $z
091c87b1 2064 _log_int(X,N) calculate integer log() of X in base N
2065 X >= 0, N >= 0 (return undef for NaN)
8df1e0a2 2066 returns (RESULT, EXACT) where EXACT is:
2067 1 : result is exactly RESULT
2068 0 : result was truncated to RESULT
2069 undef : unknown whether result is exactly RESULT
9b924220 2070 _gcd(obj,obj) return Greatest Common Divisor of two objects
2071
2072The following functions are optional, and can be defined if the underlying lib
2073has a fast way to do them. If undefined, Math::BigInt will use pure Perl (hence
2074slow) fallback routines to emulate these:
2075
2076 _signed_or
2077 _signed_and
2078 _signed_xor
2079
0716bf9b 2080
b22b3e31 2081Input strings come in as unsigned but with prefix (i.e. as '123', '0xabc'
0716bf9b 2082or '0b1101').
2083
990fb837 2084So the library needs only to deal with unsigned big integers. Testing of input
2085parameter validity is done by the caller, so you need not worry about
2086underflow (f.i. in C<_sub()>, C<_dec()>) nor about division by zero or similar
2087cases.
574bacfe 2088
2089The first parameter can be modified, that includes the possibility that you
2090return a reference to a completely different object instead. Although keeping
dccbb853 2091the reference and just changing it's contents is prefered over creating and
2092returning a different reference.
574bacfe 2093
990fb837 2094Return values are always references to objects, strings, or true/false for
2095comparisation routines.
2096
574bacfe 2097=head1 WRAP YOUR OWN
2098
2099If you want to port your own favourite c-lib for big numbers to the
2100Math::BigInt interface, you can take any of the already existing modules as
2101a rough guideline. You should really wrap up the latest BigInt and BigFloat
bd05a461 2102testsuites with your module, and replace in them any of the following:
574bacfe 2103
2104 use Math::BigInt;
2105
bd05a461 2106by this:
574bacfe 2107
2108 use Math::BigInt lib => 'yourlib';
2109
2110This way you ensure that your library really works 100% within Math::BigInt.
0716bf9b 2111
2112=head1 LICENSE
2113
2114This program is free software; you may redistribute it and/or modify it under
2115the same terms as Perl itself.
2116
2117=head1 AUTHORS
2118
2119Original math code by Mark Biggar, rewritten by Tels L<http://bloodgate.com/>
990fb837 2120in late 2000.
0716bf9b 2121Seperated from BigInt and shaped API with the help of John Peacock.
b68b7ab1 2122
2123Fixed, speed-up, streamlined and enhanced by Tels 2001 - 2005.
0716bf9b 2124
2125=head1 SEE ALSO
2126
ee15d750 2127L<Math::BigInt>, L<Math::BigFloat>, L<Math::BigInt::BitVect>,
990fb837 2128L<Math::BigInt::GMP>, L<Math::BigInt::FastCalc> and L<Math::BigInt::Pari>.
0716bf9b 2129
2130=cut