xref: /openbsd-src/gnu/usr.bin/perl/cpan/Math-BigInt/t/Math/BigInt/Lib/Minimal.pm (revision 256a93a44f36679bee503f12e49566c2183f6181)
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