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