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