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