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