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