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