15759b3d2Safresh1# This is a rather minimalistic library, whose purpose is to test inheritance 25759b3d2Safresh1# from its parent class. 35759b3d2Safresh1 45759b3d2Safresh1package Math::BigInt::Lib::Minimal; 55759b3d2Safresh1 65759b3d2Safresh1use 5.006001; 75759b3d2Safresh1use strict; 85759b3d2Safresh1use warnings; 95759b3d2Safresh1 105759b3d2Safresh1use Carp; 115759b3d2Safresh1use Math::BigInt::Lib; 125759b3d2Safresh1 135759b3d2Safresh1our @ISA = ('Math::BigInt::Lib'); 145759b3d2Safresh1 15*256a93a4Safresh1my $BASE_LEN = 5; 165759b3d2Safresh1my $BASE = 0 + ("1" . ("0" x $BASE_LEN)); 175759b3d2Safresh1my $MAX_VAL = $BASE - 1; 185759b3d2Safresh1 195759b3d2Safresh1sub _new { 205759b3d2Safresh1 my ($class, $str) = @_; 215759b3d2Safresh1 croak "Invalid input string '$str'" unless $str =~ /^([1-9]\d*|0)\z/; 225759b3d2Safresh1 235759b3d2Safresh1 my $n = length $str; 245759b3d2Safresh1 my $p = int($n / $BASE_LEN); 255759b3d2Safresh1 my $q = $n % $BASE_LEN; 265759b3d2Safresh1 27*256a93a4Safresh1 my $format = $] < 5.008 ? "a$BASE_LEN" x $p 285759b3d2Safresh1 : "(a$BASE_LEN)*"; 295759b3d2Safresh1 $format = "a$q" . $format if $q > 0; 305759b3d2Safresh1 315759b3d2Safresh1 my $self = [ reverse(map { 0 + $_ } unpack($format, $str)) ]; 325759b3d2Safresh1 return bless $self, $class; 335759b3d2Safresh1} 345759b3d2Safresh1 355759b3d2Safresh1############################################################################## 36*256a93a4Safresh1# convert to string 375759b3d2Safresh1 385759b3d2Safresh1sub _str { 395759b3d2Safresh1 my ($class, $x) = @_; 405759b3d2Safresh1 my $idx = $#$x; # index of last element 415759b3d2Safresh1 425759b3d2Safresh1 # Handle first one differently, since it should not have any leading zeros. 435759b3d2Safresh1 445759b3d2Safresh1 my $str = int($x->[$idx]); 455759b3d2Safresh1 465759b3d2Safresh1 if ($idx > 0) { 475759b3d2Safresh1 my $z = '0' x ($BASE_LEN - 1); 485759b3d2Safresh1 while (--$idx >= 0) { 495759b3d2Safresh1 $str .= substr($z . $x->[$idx], -$BASE_LEN); 505759b3d2Safresh1 } 515759b3d2Safresh1 } 525759b3d2Safresh1 $str; 535759b3d2Safresh1} 545759b3d2Safresh1 555759b3d2Safresh1############################################################################## 565759b3d2Safresh1# actual math code 575759b3d2Safresh1 585759b3d2Safresh1sub _add { 595759b3d2Safresh1 # (ref to int_num_array, ref to int_num_array) 605759b3d2Safresh1 # 615759b3d2Safresh1 # Routine to add two base 1eX numbers stolen from Knuth Vol 2 Algorithm A 625759b3d2Safresh1 # pg 231. There are separate routines to add and sub as per Knuth pg 233. 635759b3d2Safresh1 # This routine modifies array x, but not y. 645759b3d2Safresh1 655759b3d2Safresh1 my ($c, $x, $y) = @_; 665759b3d2Safresh1 675759b3d2Safresh1 # $x + 0 => $x 685759b3d2Safresh1 695759b3d2Safresh1 return $x if @$y == 1 && $y->[0] == 0; 705759b3d2Safresh1 715759b3d2Safresh1 # 0 + $y => $y->copy 725759b3d2Safresh1 735759b3d2Safresh1 if (@$x == 1 && $x->[0] == 0) { 745759b3d2Safresh1 @$x = @$y; 755759b3d2Safresh1 return $x; 765759b3d2Safresh1 } 775759b3d2Safresh1 785759b3d2Safresh1 # For each in Y, add Y to X and carry. If after that, something is left in 795759b3d2Safresh1 # X, foreach in X add carry to X and then return X, carry. Trades one 805759b3d2Safresh1 # "$j++" for having to shift arrays. 815759b3d2Safresh1 825759b3d2Safresh1 my $i; 835759b3d2Safresh1 my $car = 0; 845759b3d2Safresh1 my $j = 0; 855759b3d2Safresh1 for $i (@$y) { 865759b3d2Safresh1 $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0; 875759b3d2Safresh1 $j++; 885759b3d2Safresh1 } 895759b3d2Safresh1 while ($car != 0) { 905759b3d2Safresh1 $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0; 915759b3d2Safresh1 $j++; 925759b3d2Safresh1 } 935759b3d2Safresh1 945759b3d2Safresh1 $x; 955759b3d2Safresh1} 965759b3d2Safresh1 975759b3d2Safresh1sub _sub { 985759b3d2Safresh1 # (ref to int_num_array, ref to int_num_array, swap) 995759b3d2Safresh1 # 1005759b3d2Safresh1 # Subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y 1015759b3d2Safresh1 # subtract Y from X by modifying x in place 1025759b3d2Safresh1 my ($c, $sx, $sy, $s) = @_; 1035759b3d2Safresh1 1045759b3d2Safresh1 my $car = 0; 1055759b3d2Safresh1 my $i; 1065759b3d2Safresh1 my $j = 0; 1075759b3d2Safresh1 if (!$s) { 1085759b3d2Safresh1 for $i (@$sx) { 1095759b3d2Safresh1 last unless defined $sy->[$j] || $car; 1105759b3d2Safresh1 $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0); 1115759b3d2Safresh1 $j++; 1125759b3d2Safresh1 } 1135759b3d2Safresh1 # might leave leading zeros, so fix that 1145759b3d2Safresh1 return __strip_zeros($sx); 1155759b3d2Safresh1 } 1165759b3d2Safresh1 for $i (@$sx) { 1175759b3d2Safresh1 # We can't do an early out if $x < $y, since we need to copy the high 1185759b3d2Safresh1 # chunks from $y. Found by Bob Mathews. 1195759b3d2Safresh1 #last unless defined $sy->[$j] || $car; 1205759b3d2Safresh1 $sy->[$j] += $BASE 1215759b3d2Safresh1 if $car = ($sy->[$j] = $i - ($sy->[$j] || 0) - $car) < 0; 1225759b3d2Safresh1 $j++; 1235759b3d2Safresh1 } 1245759b3d2Safresh1 # might leave leading zeros, so fix that 1255759b3d2Safresh1 __strip_zeros($sy); 1265759b3d2Safresh1} 1275759b3d2Safresh1 1285759b3d2Safresh1# The following _mul function is an exact copy of _mul_use_div_64 in 1295759b3d2Safresh1# Math::BigInt::Calc. 1305759b3d2Safresh1 1315759b3d2Safresh1sub _mul { 1325759b3d2Safresh1 # (ref to int_num_array, ref to int_num_array) 1335759b3d2Safresh1 # multiply two numbers in internal representation 1345759b3d2Safresh1 # modifies first arg, second need not be different from first 1355759b3d2Safresh1 # works for 64 bit integer with "use integer" 1365759b3d2Safresh1 my ($c, $xv, $yv) = @_; 1375759b3d2Safresh1 1385759b3d2Safresh1 use integer; 1395759b3d2Safresh1 if (@$yv == 1) { 1405759b3d2Safresh1 # shortcut for two small numbers, also handles $x == 0 1415759b3d2Safresh1 if (@$xv == 1) { 1425759b3d2Safresh1 # shortcut for two very short numbers (improved by Nathan Zook) 1435759b3d2Safresh1 # works also if xv and yv are the same reference, and handles also $x == 0 1445759b3d2Safresh1 if (($xv->[0] *= $yv->[0]) >= $BASE) { 1455759b3d2Safresh1 $xv->[0] = 1465759b3d2Safresh1 $xv->[0] - ($xv->[1] = $xv->[0] / $BASE) * $BASE; 1475759b3d2Safresh1 } 1485759b3d2Safresh1 return $xv; 1495759b3d2Safresh1 } 1505759b3d2Safresh1 # $x * 0 => 0 1515759b3d2Safresh1 if ($yv->[0] == 0) { 1525759b3d2Safresh1 @$xv = (0); 1535759b3d2Safresh1 return $xv; 1545759b3d2Safresh1 } 1555759b3d2Safresh1 # multiply a large number a by a single element one, so speed up 1565759b3d2Safresh1 my $y = $yv->[0]; 1575759b3d2Safresh1 my $car = 0; 1585759b3d2Safresh1 foreach my $i (@$xv) { 1595759b3d2Safresh1 #$i = $i * $y + $car; $car = $i / $BASE; $i -= $car * $BASE; 1605759b3d2Safresh1 $i = $i * $y + $car; 1615759b3d2Safresh1 $i -= ($car = $i / $BASE) * $BASE; 1625759b3d2Safresh1 } 1635759b3d2Safresh1 push @$xv, $car if $car != 0; 1645759b3d2Safresh1 return $xv; 1655759b3d2Safresh1 } 1665759b3d2Safresh1 # shortcut for result $x == 0 => result = 0 1675759b3d2Safresh1 return $xv if ( ((@$xv == 1) && ($xv->[0] == 0)) ); 1685759b3d2Safresh1 1695759b3d2Safresh1 # since multiplying $x with $x fails, make copy in this case 1705759b3d2Safresh1 $yv = $c->_copy($xv) if $xv == $yv; # same references? 1715759b3d2Safresh1 1725759b3d2Safresh1 my @prod = (); 1735759b3d2Safresh1 my ($prod, $car, $cty, $xi, $yi); 1745759b3d2Safresh1 for $xi (@$xv) { 1755759b3d2Safresh1 $car = 0; 1765759b3d2Safresh1 $cty = 0; 1775759b3d2Safresh1 # looping through this if $xi == 0 is silly - so optimize it away! 1785759b3d2Safresh1 $xi = (shift @prod || 0), next if $xi == 0; 1795759b3d2Safresh1 for $yi (@$yv) { 1805759b3d2Safresh1 $prod = $xi * $yi + ($prod[$cty] || 0) + $car; 1815759b3d2Safresh1 $prod[$cty++] = $prod - ($car = $prod / $BASE) * $BASE; 1825759b3d2Safresh1 } 1835759b3d2Safresh1 $prod[$cty] += $car if $car; # need really to check for 0? 1845759b3d2Safresh1 $xi = shift @prod || 0; # || 0 makes v5.005_3 happy 1855759b3d2Safresh1 } 1865759b3d2Safresh1 push @$xv, @prod; 1875759b3d2Safresh1 $xv; 1885759b3d2Safresh1} 1895759b3d2Safresh1 1905759b3d2Safresh1# The following _div function is an exact copy of _div_use_div_64 in 1915759b3d2Safresh1# Math::BigInt::Calc. 1925759b3d2Safresh1 1935759b3d2Safresh1sub _div { 1945759b3d2Safresh1 # ref to array, ref to array, modify first array and return remainder if 1955759b3d2Safresh1 # in list context 1965759b3d2Safresh1 # This version works on 64 bit integers 1975759b3d2Safresh1 my ($c, $x, $yorg) = @_; 1985759b3d2Safresh1 1995759b3d2Safresh1 use integer; 2005759b3d2Safresh1 # the general div algorithm here is about O(N*N) and thus quite slow, so 2015759b3d2Safresh1 # we first check for some special cases and use shortcuts to handle them. 2025759b3d2Safresh1 2035759b3d2Safresh1 # This works, because we store the numbers in a chunked format where each 2045759b3d2Safresh1 # element contains 5..7 digits (depending on system). 2055759b3d2Safresh1 2065759b3d2Safresh1 # if both numbers have only one element: 2075759b3d2Safresh1 if (@$x == 1 && @$yorg == 1) { 2085759b3d2Safresh1 # shortcut, $yorg and $x are two small numbers 2095759b3d2Safresh1 if (wantarray) { 2105759b3d2Safresh1 my $rem = [ $x->[0] % $yorg->[0] ]; 2115759b3d2Safresh1 bless $rem, $c; 2125759b3d2Safresh1 $x->[0] = int($x->[0] / $yorg->[0]); 2135759b3d2Safresh1 return ($x, $rem); 2145759b3d2Safresh1 } else { 2155759b3d2Safresh1 $x->[0] = int($x->[0] / $yorg->[0]); 2165759b3d2Safresh1 return $x; 2175759b3d2Safresh1 } 2185759b3d2Safresh1 } 2195759b3d2Safresh1 # if x has more than one, but y has only one element: 2205759b3d2Safresh1 if (@$yorg == 1) { 2215759b3d2Safresh1 my $rem; 2225759b3d2Safresh1 $rem = $c->_mod($c->_copy($x), $yorg) if wantarray; 2235759b3d2Safresh1 2245759b3d2Safresh1 # shortcut, $y is < $BASE 2255759b3d2Safresh1 my $j = @$x; 2265759b3d2Safresh1 my $r = 0; 2275759b3d2Safresh1 my $y = $yorg->[0]; 2285759b3d2Safresh1 my $b; 2295759b3d2Safresh1 while ($j-- > 0) { 2305759b3d2Safresh1 $b = $r * $BASE + $x->[$j]; 2315759b3d2Safresh1 $x->[$j] = int($b/$y); 2325759b3d2Safresh1 $r = $b % $y; 2335759b3d2Safresh1 } 2345759b3d2Safresh1 pop @$x if @$x > 1 && $x->[-1] == 0; # splice up a leading zero 2355759b3d2Safresh1 return ($x, $rem) if wantarray; 2365759b3d2Safresh1 return $x; 2375759b3d2Safresh1 } 2385759b3d2Safresh1 # now x and y have more than one element 2395759b3d2Safresh1 2405759b3d2Safresh1 # check whether y has more elements than x, if yet, the result will be 0 2415759b3d2Safresh1 if (@$yorg > @$x) { 2425759b3d2Safresh1 my $rem; 2435759b3d2Safresh1 $rem = $c->_copy($x) if wantarray; # make copy 2445759b3d2Safresh1 @$x = 0; # set to 0 2455759b3d2Safresh1 return ($x, $rem) if wantarray; # including remainder? 2465759b3d2Safresh1 return $x; # only x, which is [0] now 2475759b3d2Safresh1 } 2485759b3d2Safresh1 # check whether the numbers have the same number of elements, in that case 2495759b3d2Safresh1 # the result will fit into one element and can be computed efficiently 2505759b3d2Safresh1 if (@$yorg == @$x) { 2515759b3d2Safresh1 my $rem; 2525759b3d2Safresh1 # if $yorg has more digits than $x (it's leading element is longer than 2535759b3d2Safresh1 # the one from $x), the result will also be 0: 2545759b3d2Safresh1 if (length(int($yorg->[-1])) > length(int($x->[-1]))) { 2555759b3d2Safresh1 $rem = $c->_copy($x) if wantarray; # make copy 2565759b3d2Safresh1 @$x = 0; # set to 0 2575759b3d2Safresh1 return ($x, $rem) if wantarray; # including remainder? 2585759b3d2Safresh1 return $x; 2595759b3d2Safresh1 } 2605759b3d2Safresh1 # now calculate $x / $yorg 2615759b3d2Safresh1 2625759b3d2Safresh1 if (length(int($yorg->[-1])) == length(int($x->[-1]))) { 2635759b3d2Safresh1 # same length, so make full compare 2645759b3d2Safresh1 2655759b3d2Safresh1 my $a = 0; 2665759b3d2Safresh1 my $j = @$x - 1; 2675759b3d2Safresh1 # manual way (abort if unequal, good for early ne) 2685759b3d2Safresh1 while ($j >= 0) { 2695759b3d2Safresh1 last if ($a = $x->[$j] - $yorg->[$j]); 2705759b3d2Safresh1 $j--; 2715759b3d2Safresh1 } 2725759b3d2Safresh1 # $a contains the result of the compare between X and Y 2735759b3d2Safresh1 # a < 0: x < y, a == 0: x == y, a > 0: x > y 2745759b3d2Safresh1 if ($a <= 0) { 2755759b3d2Safresh1 $rem = $c->_zero(); # a = 0 => x == y => rem 0 2765759b3d2Safresh1 $rem = $c->_copy($x) if $a != 0; # a < 0 => x < y => rem = x 2775759b3d2Safresh1 @$x = 0; # if $a < 0 2785759b3d2Safresh1 $x->[0] = 1 if $a == 0; # $x == $y 2795759b3d2Safresh1 return ($x, $rem) if wantarray; # including remainder? 2805759b3d2Safresh1 return $x; 2815759b3d2Safresh1 } 2825759b3d2Safresh1 # $x >= $y, so proceed normally 2835759b3d2Safresh1 } 2845759b3d2Safresh1 } 2855759b3d2Safresh1 2865759b3d2Safresh1 # all other cases: 2875759b3d2Safresh1 2885759b3d2Safresh1 my $y = $c->_copy($yorg); # always make copy to preserve 2895759b3d2Safresh1 2905759b3d2Safresh1 my ($car, $bar, $prd, $dd, $xi, $yi, @q, $v2, $v1, @d, $tmp, $q, $u2, $u1, $u0); 2915759b3d2Safresh1 2925759b3d2Safresh1 $car = $bar = $prd = 0; 2935759b3d2Safresh1 if (($dd = int($BASE / ($y->[-1] + 1))) != 1) { 2945759b3d2Safresh1 for $xi (@$x) { 2955759b3d2Safresh1 $xi = $xi * $dd + $car; 2965759b3d2Safresh1 $xi -= ($car = int($xi / $BASE)) * $BASE; 2975759b3d2Safresh1 } 2985759b3d2Safresh1 push(@$x, $car); 2995759b3d2Safresh1 $car = 0; 3005759b3d2Safresh1 for $yi (@$y) { 3015759b3d2Safresh1 $yi = $yi * $dd + $car; 3025759b3d2Safresh1 $yi -= ($car = int($yi / $BASE)) * $BASE; 3035759b3d2Safresh1 } 3045759b3d2Safresh1 } else { 3055759b3d2Safresh1 push(@$x, 0); 3065759b3d2Safresh1 } 3075759b3d2Safresh1 3085759b3d2Safresh1 # @q will accumulate the final result, $q contains the current computed 3095759b3d2Safresh1 # part of the final result 3105759b3d2Safresh1 3115759b3d2Safresh1 @q = (); 3125759b3d2Safresh1 ($v2, $v1) = @$y[-2, -1]; 3135759b3d2Safresh1 $v2 = 0 unless $v2; 3145759b3d2Safresh1 while ($#$x > $#$y) { 3155759b3d2Safresh1 ($u2, $u1, $u0) = @$x[-3..-1]; 3165759b3d2Safresh1 $u2 = 0 unless $u2; 3175759b3d2Safresh1 #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n" 3185759b3d2Safresh1 # if $v1 == 0; 3195759b3d2Safresh1 $q = (($u0 == $v1) ? $MAX_VAL : int(($u0 * $BASE + $u1) / $v1)); 3205759b3d2Safresh1 --$q while ($v2 * $q > ($u0 * $BASE +$ u1- $q*$v1) * $BASE + $u2); 3215759b3d2Safresh1 if ($q) { 3225759b3d2Safresh1 ($car, $bar) = (0, 0); 3235759b3d2Safresh1 for ($yi = 0, $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) { 3245759b3d2Safresh1 $prd = $q * $y->[$yi] + $car; 3255759b3d2Safresh1 $prd -= ($car = int($prd / $BASE)) * $BASE; 3265759b3d2Safresh1 $x->[$xi] += $BASE if ($bar = (($x->[$xi] -= $prd + $bar) < 0)); 3275759b3d2Safresh1 } 3285759b3d2Safresh1 if ($x->[-1] < $car + $bar) { 3295759b3d2Safresh1 $car = 0; 3305759b3d2Safresh1 --$q; 3315759b3d2Safresh1 for ($yi = 0, $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) { 3325759b3d2Safresh1 $x->[$xi] -= $BASE 3335759b3d2Safresh1 if ($car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE)); 3345759b3d2Safresh1 } 3355759b3d2Safresh1 } 3365759b3d2Safresh1 } 3375759b3d2Safresh1 pop(@$x); 3385759b3d2Safresh1 unshift(@q, $q); 3395759b3d2Safresh1 } 3405759b3d2Safresh1 if (wantarray) { 3415759b3d2Safresh1 my $d = bless [], $c; 3425759b3d2Safresh1 if ($dd != 1) { 3435759b3d2Safresh1 $car = 0; 3445759b3d2Safresh1 for $xi (reverse @$x) { 3455759b3d2Safresh1 $prd = $car * $BASE + $xi; 3465759b3d2Safresh1 $car = $prd - ($tmp = int($prd / $dd)) * $dd; 3475759b3d2Safresh1 unshift(@$d, $tmp); 3485759b3d2Safresh1 } 3495759b3d2Safresh1 } else { 3505759b3d2Safresh1 @$d = @$x; 3515759b3d2Safresh1 } 3525759b3d2Safresh1 @$x = @q; 3535759b3d2Safresh1 __strip_zeros($x); 3545759b3d2Safresh1 __strip_zeros($d); 3555759b3d2Safresh1 return ($x, $d); 3565759b3d2Safresh1 } 3575759b3d2Safresh1 @$x = @q; 3585759b3d2Safresh1 __strip_zeros($x); 3595759b3d2Safresh1 $x; 3605759b3d2Safresh1} 3615759b3d2Safresh1 3625759b3d2Safresh1# The following _mod function is an exact copy of _mod in Math::BigInt::Calc. 3635759b3d2Safresh1 3645759b3d2Safresh1sub _mod { 3655759b3d2Safresh1 # if possible, use mod shortcut 3665759b3d2Safresh1 my ($c, $x, $yo) = @_; 3675759b3d2Safresh1 3685759b3d2Safresh1 # slow way since $y too big 3695759b3d2Safresh1 if (@$yo > 1) { 3705759b3d2Safresh1 my ($xo, $rem) = $c->_div($x, $yo); 3715759b3d2Safresh1 @$x = @$rem; 3725759b3d2Safresh1 return $x; 3735759b3d2Safresh1 } 3745759b3d2Safresh1 3755759b3d2Safresh1 my $y = $yo->[0]; 3765759b3d2Safresh1 3775759b3d2Safresh1 # if both are single element arrays 3785759b3d2Safresh1 if (@$x == 1) { 3795759b3d2Safresh1 $x->[0] %= $y; 3805759b3d2Safresh1 return $x; 3815759b3d2Safresh1 } 3825759b3d2Safresh1 3835759b3d2Safresh1 # if @$x has more than one element, but @$y is a single element 3845759b3d2Safresh1 my $b = $BASE % $y; 3855759b3d2Safresh1 if ($b == 0) { 3865759b3d2Safresh1 # when BASE % Y == 0 then (B * BASE) % Y == 0 3875759b3d2Safresh1 # (B * BASE) % $y + A % Y => A % Y 3885759b3d2Safresh1 # so need to consider only last element: O(1) 3895759b3d2Safresh1 $x->[0] %= $y; 3905759b3d2Safresh1 } elsif ($b == 1) { 3915759b3d2Safresh1 # else need to go through all elements in @$x: O(N), but loop is a bit 3925759b3d2Safresh1 # simplified 3935759b3d2Safresh1 my $r = 0; 3945759b3d2Safresh1 foreach (@$x) { 3955759b3d2Safresh1 $r = ($r + $_) % $y; # not much faster, but heh... 3965759b3d2Safresh1 #$r += $_ % $y; $r %= $y; 3975759b3d2Safresh1 } 3985759b3d2Safresh1 $r = 0 if $r == $y; 3995759b3d2Safresh1 $x->[0] = $r; 4005759b3d2Safresh1 } else { 4015759b3d2Safresh1 # else need to go through all elements in @$x: O(N) 4025759b3d2Safresh1 my $r = 0; 4035759b3d2Safresh1 my $bm = 1; 4045759b3d2Safresh1 foreach (@$x) { 4055759b3d2Safresh1 $r = ($_ * $bm + $r) % $y; 4065759b3d2Safresh1 $bm = ($bm * $b) % $y; 4075759b3d2Safresh1 4085759b3d2Safresh1 #$r += ($_ % $y) * $bm; 4095759b3d2Safresh1 #$bm *= $b; 4105759b3d2Safresh1 #$bm %= $y; 4115759b3d2Safresh1 #$r %= $y; 4125759b3d2Safresh1 } 4135759b3d2Safresh1 $r = 0 if $r == $y; 4145759b3d2Safresh1 $x->[0] = $r; 4155759b3d2Safresh1 } 4165759b3d2Safresh1 @$x = $x->[0]; # keep one element of @$x 4175759b3d2Safresh1 return $x; 4185759b3d2Safresh1} 4195759b3d2Safresh1 4205759b3d2Safresh1sub __strip_zeros { 4215759b3d2Safresh1 # Internal normalization function that strips leading zeros from the array. 4225759b3d2Safresh1 # Args: ref to array 4235759b3d2Safresh1 my $x = shift; 4245759b3d2Safresh1 4255759b3d2Safresh1 push @$x, 0 if @$x == 0; # div might return empty results, so fix it 4265759b3d2Safresh1 return $x if @$x == 1; # early out 4275759b3d2Safresh1 4285759b3d2Safresh1 #print "strip: cnt $cnt i $i\n"; 4295759b3d2Safresh1 # '0', '3', '4', '0', '0', 4305759b3d2Safresh1 # 0 1 2 3 4 4315759b3d2Safresh1 # cnt = 5, i = 4 4325759b3d2Safresh1 # i = 4 4335759b3d2Safresh1 # i = 3 4345759b3d2Safresh1 # => fcnt = cnt - i (5-2 => 3, cnt => 5-1 = 4, throw away from 4th pos) 4355759b3d2Safresh1 # >= 1: skip first part (this can be zero) 4365759b3d2Safresh1 4375759b3d2Safresh1 my $i = $#$x; 4385759b3d2Safresh1 while ($i > 0) { 4395759b3d2Safresh1 last if $x->[$i] != 0; 4405759b3d2Safresh1 $i--; 4415759b3d2Safresh1 } 4425759b3d2Safresh1 $i++; 4435759b3d2Safresh1 splice(@$x, $i) if $i < @$x; 4445759b3d2Safresh1 $x; 4455759b3d2Safresh1} 4465759b3d2Safresh1 4475759b3d2Safresh1############################################################################### 4485759b3d2Safresh1# check routine to test internal state for corruptions 4495759b3d2Safresh1 4505759b3d2Safresh1sub _check { 4515759b3d2Safresh1 # used by the test suite 4525759b3d2Safresh1 my ($class, $x) = @_; 4535759b3d2Safresh1 4545759b3d2Safresh1 return "Undefined" unless defined $x; 4555759b3d2Safresh1 return "$x is not a reference" unless ref($x); 4565759b3d2Safresh1 return "Not an '$class'" unless ref($x) eq $class; 4575759b3d2Safresh1 4585759b3d2Safresh1 for (my $i = 0 ; $i <= $#$x ; ++ $i) { 4595759b3d2Safresh1 my $e = $x -> [$i]; 4605759b3d2Safresh1 4615759b3d2Safresh1 return "Element at index $i is undefined" 4625759b3d2Safresh1 unless defined $e; 4635759b3d2Safresh1 4645759b3d2Safresh1 return "Element at index $i is a '" . ref($e) . 4655759b3d2Safresh1 "', which is not a scalar" 4665759b3d2Safresh1 unless ref($e) eq ""; 4675759b3d2Safresh1 4685759b3d2Safresh1 return "Element at index $i is '$e', which does not look like an" . 4695759b3d2Safresh1 " normal integer" 4705759b3d2Safresh1 #unless $e =~ /^([1-9]\d*|0)\z/; 4715759b3d2Safresh1 unless $e =~ /^\d+\z/; 4725759b3d2Safresh1 4735759b3d2Safresh1 return "Element at index $i is '$e', which is negative" 4745759b3d2Safresh1 if $e < 0; 4755759b3d2Safresh1 4765759b3d2Safresh1 return "Element at index $i is '$e', which is not smaller than" . 4775759b3d2Safresh1 " the base '$BASE'" 4785759b3d2Safresh1 if $e >= $BASE; 4795759b3d2Safresh1 4805759b3d2Safresh1 return "Element at index $i (last element) is zero" 4815759b3d2Safresh1 if $#$x > 0 && $i == $#$x && $e == 0; 4825759b3d2Safresh1 } 4835759b3d2Safresh1 4845759b3d2Safresh1 return 0; 4855759b3d2Safresh1} 4865759b3d2Safresh1 4875759b3d2Safresh11; 488