[perl #24506] [PATCH] cannot weaken refs to read only values
[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;
8df1e0a2 1354 return ($x,1)
091c87b1 1355 }
1356 # X < BASE
1357 if ($cmp < 0)
1358 {
1359 splice (@$x,1); $x->[0] = 0;
8df1e0a2 1360 return ($x,undef);
091c87b1 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
8df1e0a2 1367 splice(@$x,1); $x->[0] = 1; # keep ref to $x
1368
1369 # simple loop that increments $x by two in each step, possible overstepping
1370 # the real result by one
091c87b1 1371
1372 # use a loop that keeps $x as scalar as long as possible (this is faster)
8df1e0a2 1373 my $trial = _copy($c,$base); my $a;
1374 my $base_mul = _mul($c, _copy($c,$base), $base);
1375
1376 while (($a = _acmp($x,$trial,$x_org)) < 0)
091c87b1 1377 {
8df1e0a2 1378 _mul($c,$trial,$base_mul); _add($c, $x, [2]);
091c87b1 1379 }
8df1e0a2 1380
1381 my $exact = 1;
1382 if ($a > 0)
091c87b1 1383 {
8df1e0a2 1384 # overstepped the result
1385 _dec($c, $x);
1386 _div($c,$trial,$base);
1387 $a = _acmp($x,$trial,$x_org);
1388 if ($a > 0)
091c87b1 1389 {
8df1e0a2 1390 _dec($c, $x);
091c87b1 1391 }
8df1e0a2 1392 $exact = 0 if $a != 0;
091c87b1 1393 }
1394
8df1e0a2 1395 ($x,$exact); # return result
b3abae2a 1396 }
1397
56d9de68 1398# for debugging:
1399 use constant DEBUG => 0;
1400 my $steps = 0;
1401 sub steps { $steps };
b3abae2a 1402
1403sub _sqrt
0716bf9b 1404 {
56d9de68 1405 # square-root of $x in place
990fb837 1406 # Compute a guess of the result (by rule of thumb), then improve it via
56d9de68 1407 # Newton's method.
394e6ffb 1408 my ($c,$x) = @_;
0716bf9b 1409
394e6ffb 1410 if (scalar @$x == 1)
1411 {
56d9de68 1412 # fit's into one Perl scalar, so result can be computed directly
394e6ffb 1413 $x->[0] = int(sqrt($x->[0]));
1414 return $x;
1415 }
1416 my $y = _copy($c,$x);
b3abae2a 1417 # hopefully _len/2 is < $BASE, the -1 is to always undershot the guess
1418 # since our guess will "grow"
1419 my $l = int((_len($c,$x)-1) / 2);
1420
56d9de68 1421 my $lastelem = $x->[-1]; # for guess
b3abae2a 1422 my $elems = scalar @$x - 1;
1423 # not enough digits, but could have more?
56d9de68 1424 if ((length($lastelem) <= 3) && ($elems > 1))
b3abae2a 1425 {
1426 # right-align with zero pad
1427 my $len = length($lastelem) & 1;
1428 print "$lastelem => " if DEBUG;
1429 $lastelem .= substr($x->[-2] . '0' x $BASE_LEN,0,$BASE_LEN);
1430 # former odd => make odd again, or former even to even again
56d9de68 1431 $lastelem = $lastelem / 10 if (length($lastelem) & 1) != $len;
b3abae2a 1432 print "$lastelem\n" if DEBUG;
1433 }
0716bf9b 1434
61f5c3f5 1435 # construct $x (instead of _lsft($c,$x,$l,10)
1436 my $r = $l % $BASE_LEN; # 10000 00000 00000 00000 ($BASE_LEN=5)
1437 $l = int($l / $BASE_LEN);
b3abae2a 1438 print "l = $l " if DEBUG;
56d9de68 1439
1440 splice @$x,$l; # keep ref($x), but modify it
1441
b3abae2a 1442 # we make the first part of the guess not '1000...0' but int(sqrt($lastelem))
1443 # that gives us:
56d9de68 1444 # 14400 00000 => sqrt(14400) => guess first digits to be 120
1445 # 144000 000000 => sqrt(144000) => guess 379
b3abae2a 1446
b3abae2a 1447 print "$lastelem (elems $elems) => " if DEBUG;
1448 $lastelem = $lastelem / 10 if ($elems & 1 == 1); # odd or even?
1449 my $g = sqrt($lastelem); $g =~ s/\.//; # 2.345 => 2345
1450 $r -= 1 if $elems & 1 == 0; # 70 => 7
1451
1452 # padd with zeros if result is too short
1453 $x->[$l--] = int(substr($g . '0' x $r,0,$r+1));
1454 print "now ",$x->[-1] if DEBUG;
1455 print " would have been ", int('1' . '0' x $r),"\n" if DEBUG;
56d9de68 1456
b3abae2a 1457 # If @$x > 1, we could compute the second elem of the guess, too, to create
56d9de68 1458 # an even better guess. Not implemented yet. Does it improve performance?
b3abae2a 1459 $x->[$l--] = 0 while ($l >= 0); # all other digits of guess are zero
56d9de68 1460
b3abae2a 1461 print "start x= ",${_str($c,$x)},"\n" if DEBUG;
394e6ffb 1462 my $two = _two();
1463 my $last = _zero();
1464 my $lastlast = _zero();
b3abae2a 1465 $steps = 0 if DEBUG;
394e6ffb 1466 while (_acmp($c,$last,$x) != 0 && _acmp($c,$lastlast,$x) != 0)
1467 {
b3abae2a 1468 $steps++ if DEBUG;
394e6ffb 1469 $lastlast = _copy($c,$last);
1470 $last = _copy($c,$x);
1471 _add($c,$x, _div($c,_copy($c,$y),$x));
1472 _div($c,$x, $two );
56d9de68 1473 print " x= ",${_str($c,$x)},"\n" if DEBUG;
394e6ffb 1474 }
b3abae2a 1475 print "\nsteps in sqrt: $steps, " if DEBUG;
394e6ffb 1476 _dec($c,$x) if _acmp($c,$y,_mul($c,_copy($c,$x),$x)) < 0; # overshot?
b3abae2a 1477 print " final ",$x->[-1],"\n" if DEBUG;
394e6ffb 1478 $x;
0716bf9b 1479 }
1480
990fb837 1481sub _root
1482 {
1483 # take n'th root of $x in place (n >= 3)
990fb837 1484 my ($c,$x,$n) = @_;
1485
1486 if (scalar @$x == 1)
1487 {
1488 if (scalar @$n > 1)
1489 {
1490 # result will always be smaller than 2 so trunc to 1 at once
1491 $x->[0] = 1;
1492 }
1493 else
1494 {
1495 # fit's into one Perl scalar, so result can be computed directly
091c87b1 1496 # cannot use int() here, because it rounds wrongly (try
1497 # (81 ** 3) ** (1/3) to see what I mean)
1498 #$x->[0] = int( $x->[0] ** (1 / $n->[0]) );
1499 # round to 8 digits, then truncate result to integer
1500 $x->[0] = int ( sprintf ("%.8f", $x->[0] ** (1 / $n->[0]) ) );
990fb837 1501 }
1502 return $x;
1503 }
1504
c38b2de2 1505 # X is more than one element
1506 # if $n is a power of two, we can repeatedly take sqrt($X) and find the
1507 # proper result, because sqrt(sqrt($x)) == root($x,4)
1508 my $b = _as_bin($c,$n);
1509 if ($$b =~ /0b1(0+)/)
1510 {
1511 my $count = CORE::length($1); # 0b100 => len('00') => 2
1512 my $cnt = $count; # counter for loop
1513 unshift (@$x, 0); # add one element, together with one
1514 # more below in the loop this makes 2
1515 while ($cnt-- > 0)
1516 {
1517 # 'inflate' $X by adding one element, basically computing
1518 # $x * $BASE * $BASE. This gives us more $BASE_LEN digits for result
1519 # since len(sqrt($X)) approx == len($x) / 2.
1520 unshift (@$x, 0);
1521 # calculate sqrt($x), $x is now one element to big, again. In the next
1522 # round we make that two, again.
1523 _sqrt($c,$x);
1524 }
1525 # $x is now one element to big, so truncate result by removing it
1526 splice (@$x,0,1);
1527 }
1528 else
1529 {
091c87b1 1530 # trial computation by starting with 2,4,8,16 etc until we overstep
1531
1532 my $step = _two();
1533 my $trial = _two();
1534
1535 _mul($c, $trial, $step)
1536 while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0);
1537
1538 # hit exactly?
1539 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) == 0)
1540 {
1541 @$x = @$trial; # make copy while preserving ref to $x
1542 return $x;
1543 }
1544 # overstepped, so go back on step
1545 _div($c, $trial, $step);
1546
1547 # add two, because $trial cannot be exactly the result (otherwise we would
1548 # alrady have found it)
1549 _add($c, $trial, $step);
1550
1551 # and now add more and more (2,4,6,8, etc)
1552 _add($c, $trial, $step)
1553 while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0);
1554
1555 # hit not exactly? (overstepped)
1556 # 80 too small, 81 slightly too big, 82 too big
1557 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0)
1558 {
1559 _dec($c,$trial);
1560 }
1561 # 80 too small, 81 slightly too big
1562 if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0)
1563 {
1564 _dec($c,$trial);
1565 }
1566
1567 @$x = @$trial; # make copy while preserving ref to $x
1568 return $x;
c38b2de2 1569 }
990fb837 1570 $x;
1571 }
1572
394e6ffb 1573##############################################################################
1574# binary stuff
0716bf9b 1575
394e6ffb 1576sub _and
1577 {
1578 my ($c,$x,$y) = @_;
0716bf9b 1579
394e6ffb 1580 # the shortcut makes equal, large numbers _really_ fast, and makes only a
1581 # very small performance drop for small numbers (e.g. something with less
1582 # than 32 bit) Since we optimize for large numbers, this is enabled.
1583 return $x if _acmp($c,$x,$y) == 0; # shortcut
0716bf9b 1584
394e6ffb 1585 my $m = _one(); my ($xr,$yr);
1586 my $mask = $AND_MASK;
1587
1588 my $x1 = $x;
1589 my $y1 = _copy($c,$y); # make copy
1590 $x = _zero();
1591 my ($b,$xrr,$yrr);
1592 use integer;
1593 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
1594 {
1595 ($x1, $xr) = _div($c,$x1,$mask);
1596 ($y1, $yr) = _div($c,$y1,$mask);
1597
1598 # make ints() from $xr, $yr
1599 # this is when the AND_BITS are greater tahn $BASE and is slower for
1600 # small (<256 bits) numbers, but faster for large numbers. Disabled
1601 # due to KISS principle
1602
1603# $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1604# $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
1605# _add($c,$x, _mul($c, _new( $c, \($xrr & $yrr) ), $m) );
1606
61f5c3f5 1607 # 0+ due to '&' doesn't work in strings
1608 _add($c,$x, _mul($c, [ 0+$xr->[0] & 0+$yr->[0] ], $m) );
394e6ffb 1609 _mul($c,$m,$mask);
1610 }
1611 $x;
0716bf9b 1612 }
1613
394e6ffb 1614sub _xor
0716bf9b 1615 {
394e6ffb 1616 my ($c,$x,$y) = @_;
1617
1618 return _zero() if _acmp($c,$x,$y) == 0; # shortcut (see -and)
1619
1620 my $m = _one(); my ($xr,$yr);
1621 my $mask = $XOR_MASK;
1622
1623 my $x1 = $x;
1624 my $y1 = _copy($c,$y); # make copy
1625 $x = _zero();
1626 my ($b,$xrr,$yrr);
1627 use integer;
1628 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
0716bf9b 1629 {
394e6ffb 1630 ($x1, $xr) = _div($c,$x1,$mask);
1631 ($y1, $yr) = _div($c,$y1,$mask);
1632 # make ints() from $xr, $yr (see _and())
1633 #$b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1634 #$b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
1635 #_add($c,$x, _mul($c, _new( $c, \($xrr ^ $yrr) ), $m) );
61f5c3f5 1636
1637 # 0+ due to '^' doesn't work in strings
1638 _add($c,$x, _mul($c, [ 0+$xr->[0] ^ 0+$yr->[0] ], $m) );
394e6ffb 1639 _mul($c,$m,$mask);
0716bf9b 1640 }
394e6ffb 1641 # the loop stops when the shorter of the two numbers is exhausted
1642 # the remainder of the longer one will survive bit-by-bit, so we simple
1643 # multiply-add it in
1644 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1);
1645 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1);
1646
1647 $x;
0716bf9b 1648 }
1649
394e6ffb 1650sub _or
0716bf9b 1651 {
394e6ffb 1652 my ($c,$x,$y) = @_;
0716bf9b 1653
394e6ffb 1654 return $x if _acmp($c,$x,$y) == 0; # shortcut (see _and)
0716bf9b 1655
394e6ffb 1656 my $m = _one(); my ($xr,$yr);
1657 my $mask = $OR_MASK;
0716bf9b 1658
394e6ffb 1659 my $x1 = $x;
1660 my $y1 = _copy($c,$y); # make copy
1661 $x = _zero();
1662 my ($b,$xrr,$yrr);
1663 use integer;
1664 while (!_is_zero($c,$x1) && !_is_zero($c,$y1))
1665 {
1666 ($x1, $xr) = _div($c,$x1,$mask);
1667 ($y1, $yr) = _div($c,$y1,$mask);
1668 # make ints() from $xr, $yr (see _and())
1669# $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
1670# $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
1671# _add($c,$x, _mul($c, _new( $c, \($xrr | $yrr) ), $m) );
1672
61f5c3f5 1673 # 0+ due to '|' doesn't work in strings
1674 _add($c,$x, _mul($c, [ 0+$xr->[0] | 0+$yr->[0] ], $m) );
394e6ffb 1675 _mul($c,$m,$mask);
1676 }
1677 # the loop stops when the shorter of the two numbers is exhausted
1678 # the remainder of the longer one will survive bit-by-bit, so we simple
1679 # multiply-add it in
1680 _add($c,$x, _mul($c, $x1, $m) ) if !_is_zero($c,$x1);
1681 _add($c,$x, _mul($c, $y1, $m) ) if !_is_zero($c,$y1);
1682
1683 $x;
0716bf9b 1684 }
1685
61f5c3f5 1686sub _as_hex
1687 {
1688 # convert a decimal number to hex (ref to array, return ref to string)
1689 my ($c,$x) = @_;
1690
091c87b1 1691 # fit's into one element (handle also 0x0 case)
990fb837 1692 if (@$x == 1)
1693 {
091c87b1 1694 my $t = sprintf("0x%x",$x->[0]);
990fb837 1695 return \$t;
1696 }
1697
61f5c3f5 1698 my $x1 = _copy($c,$x);
1699
1700 my $es = '';
1ddff52a 1701 my ($xr, $h, $x10000);
1702 if ($] >= 5.006)
1703 {
1704 $x10000 = [ 0x10000 ]; $h = 'h4';
1705 }
1706 else
1707 {
1708 $x10000 = [ 0x1000 ]; $h = 'h3';
1709 }
091c87b1 1710 # while (! _is_zero($c,$x1))
1711 while (@$x1 != 1 || $x1->[0] != 0) # _is_zero()
61f5c3f5 1712 {
1713 ($x1, $xr) = _div($c,$x1,$x10000);
091c87b1 1714 $es .= unpack($h,pack('v',$xr->[0])); # XXX TODO: why pack('v',...)?
61f5c3f5 1715 }
1716 $es = reverse $es;
1717 $es =~ s/^[0]+//; # strip leading zeros
1718 $es = '0x' . $es;
1719 \$es;
1720 }
1721
1722sub _as_bin
1723 {
1724 # convert a decimal number to bin (ref to array, return ref to string)
1725 my ($c,$x) = @_;
1726
091c87b1 1727 # fit's into one element (and Perl recent enough), handle also 0b0 case
1728 # handle zero case for older Perls
1729 if ($] <= 5.005 && @$x == 1 && $x->[0] == 0)
1730 {
1731 my $t = '0b0'; return \$t;
1732 }
1733 if (@$x == 1 && $] >= 5.006)
990fb837 1734 {
091c87b1 1735 my $t = sprintf("0b%b",$x->[0]);
990fb837 1736 return \$t;
1737 }
61f5c3f5 1738 my $x1 = _copy($c,$x);
1739
1740 my $es = '';
1ddff52a 1741 my ($xr, $b, $x10000);
1742 if ($] >= 5.006)
1743 {
1744 $x10000 = [ 0x10000 ]; $b = 'b16';
1745 }
1746 else
1747 {
1748 $x10000 = [ 0x1000 ]; $b = 'b12';
1749 }
091c87b1 1750 # while (! _is_zero($c,$x1))
1751 while (!(@$x1 == 1 && $x1->[0] == 0)) # _is_zero()
61f5c3f5 1752 {
1753 ($x1, $xr) = _div($c,$x1,$x10000);
091c87b1 1754 $es .= unpack($b,pack('v',$xr->[0])); # XXX TODO: why pack('v',...)?
1755 # $es .= unpack($b,$xr->[0]);
61f5c3f5 1756 }
1757 $es = reverse $es;
1758 $es =~ s/^[0]+//; # strip leading zeros
1759 $es = '0b' . $es;
1760 \$es;
1761 }
1762
394e6ffb 1763sub _from_hex
0716bf9b 1764 {
394e6ffb 1765 # convert a hex number to decimal (ref to string, return ref to array)
1766 my ($c,$hs) = @_;
0716bf9b 1767
394e6ffb 1768 my $mul = _one();
1769 my $m = [ 0x10000 ]; # 16 bit at a time
1770 my $x = _zero();
0716bf9b 1771
61f5c3f5 1772 my $len = length($$hs)-2;
394e6ffb 1773 $len = int($len/4); # 4-digit parts, w/o '0x'
1774 my $val; my $i = -4;
1775 while ($len >= 0)
1776 {
1777 $val = substr($$hs,$i,4);
1778 $val =~ s/^[+-]?0x// if $len == 0; # for last part only because
1779 $val = hex($val); # hex does not like wrong chars
1780 $i -= 4; $len --;
1781 _add ($c, $x, _mul ($c, [ $val ], $mul ) ) if $val != 0;
1782 _mul ($c, $mul, $m ) if $len >= 0; # skip last mul
1783 }
1784 $x;
1785 }
1786
1787sub _from_bin
0716bf9b 1788 {
394e6ffb 1789 # convert a hex number to decimal (ref to string, return ref to array)
1790 my ($c,$bs) = @_;
0716bf9b 1791
091c87b1 1792 # instead of converting X (8) bit at a time, it is faster to "convert" the
13a12e00 1793 # number to hex, and then call _from_hex.
1794
1795 my $hs = $$bs;
1796 $hs =~ s/^[+-]?0b//; # remove sign and 0b
1797 my $l = length($hs); # bits
1798 $hs = '0' x (8-($l % 8)) . $hs if ($l % 8) != 0; # padd left side w/ 0
1799 my $h = unpack('H*', pack ('B*', $hs)); # repack as hex
091c87b1 1800
1801 $c->_from_hex(\('0x'.$h));
0716bf9b 1802 }
1803
07d34614 1804##############################################################################
1805# special modulus functions
1806
56d9de68 1807sub _modinv
d614cd8b 1808 {
56d9de68 1809 # modular inverse
1810 my ($c,$x,$y) = @_;
1ddff52a 1811
56d9de68 1812 my $u = _zero($c); my $u1 = _one($c);
1813 my $a = _copy($c,$y); my $b = _copy($c,$x);
1ddff52a 1814
1815 # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the
56d9de68 1816 # result ($u) at the same time. See comments in BigInt for why this works.
1817 my $q;
1818 ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1
1819 my $sign = 1;
1ddff52a 1820 while (!_is_zero($c,$b))
1821 {
56d9de68 1822 my $t = _add($c, # step 2:
1823 _mul($c,_copy($c,$u1), $q) , # t = u1 * q
1824 $u ); # + u
1825 $u = $u1; # u = u1, u1 = t
1826 $u1 = $t;
1827 $sign = -$sign;
1828 ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1
1ddff52a 1829 }
1830
1831 # if the gcd is not 1, then return NaN
56d9de68 1832 return (undef,undef) unless _is_one($c,$a);
1833
1834 $sign = $sign == 1 ? '+' : '-';
1835 ($u1,$sign);
d614cd8b 1836 }
1837
1838sub _modpow
1839 {
1840 # modulus of power ($x ** $y) % $z
07d34614 1841 my ($c,$num,$exp,$mod) = @_;
1842
1843 # in the trivial case,
1844 if (_is_one($c,$mod))
1845 {
1846 splice @$num,0,1; $num->[0] = 0;
1847 return $num;
1848 }
1849 if ((scalar @$num == 1) && (($num->[0] == 0) || ($num->[0] == 1)))
1850 {
1851 $num->[0] = 1;
1852 return $num;
1853 }
1ddff52a 1854
1855# $num = _mod($c,$num,$mod); # this does not make it faster
07d34614 1856
1857 my $acc = _copy($c,$num); my $t = _one();
1858
1ddff52a 1859 my $expbin = ${_as_bin($c,$exp)}; $expbin =~ s/^0b//;
1860 my $len = length($expbin);
1861 while (--$len >= 0)
07d34614 1862 {
1ddff52a 1863 if ( substr($expbin,$len,1) eq '1') # is_odd
07d34614 1864 {
1865 _mul($c,$t,$acc);
1866 $t = _mod($c,$t,$mod);
1867 }
1868 _mul($c,$acc,$acc);
1869 $acc = _mod($c,$acc,$mod);
07d34614 1870 }
1871 @$num = @$t;
1872 $num;
d614cd8b 1873 }
1874
394e6ffb 1875##############################################################################
1876##############################################################################
1877
0716bf9b 18781;
1879__END__
1880
1881=head1 NAME
1882
1883Math::BigInt::Calc - Pure Perl module to support Math::BigInt
1884
1885=head1 SYNOPSIS
1886
ee15d750 1887Provides support for big integer calculations. Not intended to be used by other
091c87b1 1888modules. Other modules which sport the same functions can also be used to support
1889Math::BigInt, like Math::BigInt::GMP or Math::BigInt::Pari.
0716bf9b 1890
1891=head1 DESCRIPTION
1892
027dc388 1893In order to allow for multiple big integer libraries, Math::BigInt was
1894rewritten to use library modules for core math routines. Any module which
1895follows the same API as this can be used instead by using the following:
0716bf9b 1896
ee15d750 1897 use Math::BigInt lib => 'libname';
0716bf9b 1898
027dc388 1899'libname' is either the long name ('Math::BigInt::Pari'), or only the short
1900version like 'Pari'.
1901
990fb837 1902=head1 STORAGE
1903
1904=head1 METHODS
0716bf9b 1905
027dc388 1906The following functions MUST be defined in order to support the use by
1907Math::BigInt:
0716bf9b 1908
1909 _new(string) return ref to new object from ref to decimal string
1910 _zero() return a new object with value 0
1911 _one() return a new object with value 1
1912
1913 _str(obj) return ref to a string representing the object
1914 _num(obj) returns a Perl integer/floating point number
1915 NOTE: because of Perl numeric notation defaults,
1916 the _num'ified obj may lose accuracy due to
1917 machine-dependend floating point size limitations
1918
1919 _add(obj,obj) Simple addition of two objects
1920 _mul(obj,obj) Multiplication of two objects
1921 _div(obj,obj) Division of the 1st object by the 2nd
b22b3e31 1922 In list context, returns (result,remainder).
1923 NOTE: this is integer math, so no
1924 fractional part will be returned.
990fb837 1925 The second operand will be not be 0, so no need to
1926 check for that.
b22b3e31 1927 _sub(obj,obj) Simple subtraction of 1 object from another
0716bf9b 1928 a third, optional parameter indicates that the params
1929 are swapped. In this case, the first param needs to
1930 be preserved, while you can destroy the second.
1931 sub (x,y,1) => return x - y and keep x intact!
e745a66c 1932 _dec(obj) decrement object by one (input is garant. to be > 0)
1933 _inc(obj) increment object by one
1934
0716bf9b 1935
1936 _acmp(obj,obj) <=> operator for objects (return -1, 0 or 1)
1937
1938 _len(obj) returns count of the decimal digits of the object
1939 _digit(obj,n) returns the n'th decimal digit of object
1940
1941 _is_one(obj) return true if argument is +1
1942 _is_zero(obj) return true if argument is 0
1943 _is_even(obj) return true if argument is even (0,2,4,6..)
1944 _is_odd(obj) return true if argument is odd (1,3,5,7..)
1945
1946 _copy return a ref to a true copy of the object
1947
1948 _check(obj) check whether internal representation is still intact
1949 return 0 for ok, otherwise error message as string
1950
bd05a461 1951The following functions are optional, and can be defined if the underlying lib
027dc388 1952has a fast way to do them. If undefined, Math::BigInt will use pure Perl (hence
1953slow) fallback routines to emulate these:
0716bf9b 1954
1955 _from_hex(str) return ref to new object from ref to hexadecimal string
1956 _from_bin(str) return ref to new object from ref to binary string
1957
ee15d750 1958 _as_hex(str) return ref to scalar string containing the value as
1959 unsigned hex string, with the '0x' prepended.
1960 Leading zeros must be stripped.
1961 _as_bin(str) Like as_hex, only as binary string containing only
1962 zeros and ones. Leading zeros must be stripped and a
1963 '0b' must be prepended.
1964
0716bf9b 1965 _rsft(obj,N,B) shift object in base B by N 'digits' right
dccbb853 1966 For unsupported bases B, return undef to signal failure
0716bf9b 1967 _lsft(obj,N,B) shift object in base B by N 'digits' left
dccbb853 1968 For unsupported bases B, return undef to signal failure
0716bf9b 1969
1970 _xor(obj1,obj2) XOR (bit-wise) object 1 with object 2
dccbb853 1971 Note: XOR, AND and OR pad with zeros if size mismatches
0716bf9b 1972 _and(obj1,obj2) AND (bit-wise) object 1 with object 2
1973 _or(obj1,obj2) OR (bit-wise) object 1 with object 2
1974
091c87b1 1975 _signed_or
1976 _signed_and
1977 _signed_xor
1978
dccbb853 1979 _mod(obj,obj) Return remainder of div of the 1st by the 2nd object
990fb837 1980 _sqrt(obj) return the square root of object (truncated to int)
1981 _root(obj) return the n'th (n >= 3) root of obj (truncated to int)
b3abae2a 1982 _fac(obj) return factorial of object 1 (1*2*3*4..)
0716bf9b 1983 _pow(obj,obj) return object 1 to the power of object 2
1984 _gcd(obj,obj) return Greatest Common Divisor of two objects
1985
b22b3e31 1986 _zeros(obj) return number of trailing decimal zeros
d614cd8b 1987 _modinv return inverse modulus
1988 _modpow return modulus of power ($x ** $y) % $z
091c87b1 1989 _log_int(X,N) calculate integer log() of X in base N
1990 X >= 0, N >= 0 (return undef for NaN)
8df1e0a2 1991 returns (RESULT, EXACT) where EXACT is:
1992 1 : result is exactly RESULT
1993 0 : result was truncated to RESULT
1994 undef : unknown whether result is exactly RESULT
0716bf9b 1995
b22b3e31 1996Input strings come in as unsigned but with prefix (i.e. as '123', '0xabc'
0716bf9b 1997or '0b1101').
1998
990fb837 1999So the library needs only to deal with unsigned big integers. Testing of input
2000parameter validity is done by the caller, so you need not worry about
2001underflow (f.i. in C<_sub()>, C<_dec()>) nor about division by zero or similar
2002cases.
574bacfe 2003
2004The first parameter can be modified, that includes the possibility that you
2005return a reference to a completely different object instead. Although keeping
dccbb853 2006the reference and just changing it's contents is prefered over creating and
2007returning a different reference.
574bacfe 2008
990fb837 2009Return values are always references to objects, strings, or true/false for
2010comparisation routines.
2011
2012Exceptions are C<_lsft()> and C<_rsft()>, which return undef if they can not
2013shift the argument. This is used to delegate shifting of bases different than
2014the one you can support back to Math::BigInt, which will use some generic code
2015to calculate the result.
574bacfe 2016
2017=head1 WRAP YOUR OWN
2018
2019If you want to port your own favourite c-lib for big numbers to the
2020Math::BigInt interface, you can take any of the already existing modules as
2021a rough guideline. You should really wrap up the latest BigInt and BigFloat
bd05a461 2022testsuites with your module, and replace in them any of the following:
574bacfe 2023
2024 use Math::BigInt;
2025
bd05a461 2026by this:
574bacfe 2027
2028 use Math::BigInt lib => 'yourlib';
2029
2030This way you ensure that your library really works 100% within Math::BigInt.
0716bf9b 2031
2032=head1 LICENSE
2033
2034This program is free software; you may redistribute it and/or modify it under
2035the same terms as Perl itself.
2036
2037=head1 AUTHORS
2038
2039Original math code by Mark Biggar, rewritten by Tels L<http://bloodgate.com/>
990fb837 2040in late 2000.
0716bf9b 2041Seperated from BigInt and shaped API with the help of John Peacock.
091c87b1 2042Fixed, sped-up and enhanced by Tels http://bloodgate.com 2001-2003.
0716bf9b 2043
2044=head1 SEE ALSO
2045
ee15d750 2046L<Math::BigInt>, L<Math::BigFloat>, L<Math::BigInt::BitVect>,
990fb837 2047L<Math::BigInt::GMP>, L<Math::BigInt::FastCalc> and L<Math::BigInt::Pari>.
0716bf9b 2048
2049=cut