Current File : //proc/self/root/usr/share/perl/5.38.2/Math/BigInt/Calc.pm
package Math::BigInt::Calc;

use 5.006001;
use strict;
use warnings;

use Carp qw< carp croak >;
use Math::BigInt::Lib;

our $VERSION = '1.999837';
$VERSION =~ tr/_//d;

our @ISA = ('Math::BigInt::Lib');

# Package to store unsigned big integers in decimal and do math with them
#
# Internally the numbers are stored in an array with at least 1 element, no
# leading zero parts (except the first) and in base 1eX where X is determined
# automatically at loading time to be the maximum possible value
#
# todo:
# - fully remove funky $# stuff in div() (maybe - that code scares me...)

##############################################################################
# global constants, flags and accessory

# constants for easier life

my $MAX_EXP_F;      # the maximum possible base 10 exponent with "no integer"
my $MAX_EXP_I;      # the maximum possible base 10 exponent with "use integer"

my $MAX_BITS;       # the maximum possible number of bits for $AND_BITS etc.

my $BASE_LEN;       # the current base exponent in use
my $USE_INT;        # whether "use integer" is used in the computations

my $BASE;           # the current base, e.g., 10000 if $BASE_LEN is 5
my $MAX_VAL;        # maximum value for an element, i.e., $BASE - 1

my $AND_BITS;       # maximum value used in binary and, e.g., 0xffff
my $OR_BITS;        # ditto for binary or
my $XOR_BITS;       # ditto for binary xor

my $AND_MASK;       # $AND_BITS + 1, e.g., 0x10000 if $AND_BITS is 0xffff
my $OR_MASK;        # ditto for binary or
my $XOR_MASK;       # ditto for binary xor

sub config {
    my $self = shift;

    croak "Missing input argument" unless @_;

    # Called as a getter.

    if (@_ == 1) {
        my $param = shift;
        croak "Parameter name must be a non-empty string"
          unless defined $param && length $param;
        return $BASE_LEN if $param eq 'base_len';
        return $USE_INT  if $param eq 'use_int';
        croak "Unknown parameter '$param'";
    }

    # Called as a setter.

    my $opts;
    while (@_) {
        my $param = shift;
        croak "Parameter name must be a non-empty string"
          unless defined $param && length $param;
        croak "Missing value for parameter '$param'"
          unless @_;
        my $value = shift;

        if ($param eq 'base_len' || $param eq 'use_int') {
            $opts -> {$param} = $value;
            next;
        }

        croak "Unknown parameter '$param'";
    }

    $BASE_LEN = $opts -> {base_len} if exists $opts -> {base_len};
    $USE_INT  = $opts -> {use_int}  if exists $opts -> {use_int};
    __PACKAGE__ -> _base_len($BASE_LEN, $USE_INT);

    return $self;
}

sub _base_len {
    #my $class = shift;                  # $class is not used
    shift;

    if (@_) {                           # if called as setter ...
        my ($base_len, $use_int) = @_;

        croak "The base length must be a positive integer"
          unless defined($base_len) && $base_len == int($base_len)
                 && $base_len > 0;

        if ( $use_int && ($base_len > $MAX_EXP_I) ||
            !$use_int && ($base_len > $MAX_EXP_F))
        {
            croak "The maximum base length (exponent) is $MAX_EXP_I with",
              " 'use integer' and $MAX_EXP_F without 'use integer'. The",
              " requested settings, a base length of $base_len ",
              $use_int ? "with" : "without", " 'use integer', is invalid.";
        }

        $BASE_LEN = $base_len;
        $BASE = 0 + ("1" . ("0" x $BASE_LEN));
        $MAX_VAL = $BASE - 1;
        $USE_INT = $use_int ? 1 : 0;

        {
            no warnings "redefine";
            if ($use_int) {
                *_mul = \&_mul_use_int;
                *_div = \&_div_use_int;
            } else {
                *_mul = \&_mul_no_int;
                *_div = \&_div_no_int;
            }
        }
    }

    # Find max bits. This is the largest power of two that is both no larger
    # than $BASE and no larger than the maximum integer (i.e., ~0). We need
    # this limitation because _and(), _or(), and _xor() only work on one
    # element at a time.

    my $umax = ~0;                      # largest unsigned integer
    my $tmp  = $umax < $BASE ? $umax : $BASE;

    $MAX_BITS = 0;
    while ($tmp >>= 1) {
        $MAX_BITS++;
    }

    # Limit to 32 bits for portability. Is this really necessary? XXX

    $MAX_BITS = 32 if $MAX_BITS > 32;

    # Find out how many bits _and, _or and _xor can take (old default = 16).
    # Are these tests really necessary? Can't we just use $MAX_BITS? XXX

    for ($AND_BITS = $MAX_BITS ; $AND_BITS > 0 ; $AND_BITS--) {
        my $x = CORE::oct('0b' . '1' x $AND_BITS);
        my $y = $x & $x;
        my $z = 2 * (2 ** ($AND_BITS - 1)) + 1;
        last unless $AND_BITS < $MAX_BITS && $x == $z && $y == $x;
    }

    for ($XOR_BITS = $MAX_BITS ; $XOR_BITS > 0 ; $XOR_BITS--) {
        my $x = CORE::oct('0b' . '1' x $XOR_BITS);
        my $y = $x ^ $x;
        my $z = 2 * (2 ** ($XOR_BITS - 1)) + 1;
        last unless $XOR_BITS < $MAX_BITS && $x == $z && $y == $x;
    }

    for ($OR_BITS = $MAX_BITS ; $OR_BITS > 0 ; $OR_BITS--) {
        my $x = CORE::oct('0b' . '1' x $OR_BITS);
        my $y = $x | $x;
        my $z = 2 * (2 ** ($OR_BITS - 1)) + 1;
        last unless $OR_BITS < $MAX_BITS && $x == $z && $y == $x;
    }

    $AND_MASK = __PACKAGE__->_new(( 2 ** $AND_BITS ));
    $XOR_MASK = __PACKAGE__->_new(( 2 ** $XOR_BITS ));
    $OR_MASK  = __PACKAGE__->_new(( 2 ** $OR_BITS  ));

    return $BASE_LEN unless wantarray;
    return ($BASE_LEN, $BASE, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL,
            $MAX_BITS, $MAX_EXP_F, $MAX_EXP_I, $USE_INT);
}

sub _new {
    # Given a string representing an integer, returns a reference to an array
    # of integers, where each integer represents a chunk of the original input
    # integer.

    my ($class, $str) = @_;
    #unless ($str =~ /^([1-9]\d*|0)\z/) {
    #    croak("Invalid input string '$str'");
    #}

    my $input_len = length($str) - 1;

    # Shortcut for small numbers.
    return bless [ $str ], $class if $input_len < $BASE_LEN;

    my $format = "a" . (($input_len % $BASE_LEN) + 1);
    $format .= $] < 5.008 ? "a$BASE_LEN" x int($input_len / $BASE_LEN)
                          : "(a$BASE_LEN)*";

    my $self = [ reverse(map { 0 + $_ } unpack($format, $str)) ];
    return bless $self, $class;
}

BEGIN {

    # Compute $MAX_EXP_F, the maximum usable base 10 exponent.

    # The largest element in base 10**$BASE_LEN is 10**$BASE_LEN-1. For instance,
    # with $BASE_LEN = 5, the largest element is 99_999, and the largest carry is
    #
    #     int( 99_999 * 99_999 / 100_000 ) = 99_998
    #
    # so make sure that 99_999 * 99_999 + 99_998 is within the range of integers
    # that can be represented accuratly.
    #
    # Note that on some systems with quadmath support, the following is within
    # the range of numbers that can be represented exactly, but it still gives
    # the incorrect value $r = 2 (even though POSIX::fmod($x, $y) gives the
    # correct value of 1:
    #
    #     $x =  99999999999999999;
    #     $y = 100000000000000000;
    #     $r = $x * $x % $y;            # should be 1
    #
    # so also check for this.

    for ($MAX_EXP_F = 1 ; ; $MAX_EXP_F++) {         # when $MAX_EXP_F = 5
        my $MAX_EXP_FM1 = $MAX_EXP_F - 1;           #   = 4
        my $bs = "1" . ("0" x $MAX_EXP_F);          #   = "100000"
        my $xs = "9" x $MAX_EXP_F;                  #   =  "99999"
        my $cs = ("9" x $MAX_EXP_FM1) . "8";        #   =  "99998"
        my $ys = $cs . ("0" x $MAX_EXP_FM1) . "1";  #   = "9999800001"

        # Compute and check the product.
        my $yn = $xs * $xs;                         #   = 9999800001
        last if $yn != $ys;

        # Compute and check the remainder.
        my $rn = $yn % $bs;                         #   = 1
        last if $rn != 1;

        # Compute and check the carry. The division here is exact.
        my $cn = ($yn - $rn) / $bs;                 #   = 99998
        last if $cn != $cs;

        # Compute and check product plus carry.
        my $zs = $cs . ("9" x $MAX_EXP_F);          #   = "9999899999"
        my $zn = $yn + $cn;                         #   = 99998999999
        last if $zn != $zs;
        last if $zn - ($zn - 1) != 1;
    }
    $MAX_EXP_F--;                       # last test failed, so retract one step

    # Compute $MAX_EXP_I, the maximum usable base 10 exponent within the range
    # of what is available with "use integer". On older versions of Perl,
    # integers are converted to floating point numbers, even though they are
    # within the range of what can be represented as integers. For example, on
    # some 64 bit Perls, 999999999 * 999999999 becomes 999999998000000000, not
    # 999999998000000001, even though the latter is less than the maximum value
    # for a 64 bit integer, 18446744073709551615.

    my $umax = ~0;                      # largest unsigned integer
    for ($MAX_EXP_I = int(0.5 * log($umax) / log(10));
         $MAX_EXP_I > 0;
         $MAX_EXP_I--)
    {                                               # when $MAX_EXP_I = 5
        my $MAX_EXP_IM1 = $MAX_EXP_I - 1;           #   = 4
        my $bs = "1" . ("0" x $MAX_EXP_I);          #   = "100000"
        my $xs = "9" x $MAX_EXP_I;                  #   =  "99999"
        my $cs = ("9" x $MAX_EXP_IM1) . "8";        #   =  "99998"
        my $ys = $cs . ("0" x $MAX_EXP_IM1) . "1";  #   = "9999800001"

        # Compute and check the product.
        my $yn = $xs * $xs;                         #   = 9999800001
        next if $yn != $ys;

        # Compute and check the remainder.
        my $rn = $yn % $bs;                         #   = 1
        next if $rn != 1;

        # Compute and check the carry. The division here is exact.
        my $cn = ($yn - $rn) / $bs;                 #   = 99998
        next if $cn != $cs;

        # Compute and check product plus carry.
        my $zs = $cs . ("9" x $MAX_EXP_I);          #   = "9999899999"
        my $zn = $yn + $cn;                         #   = 99998999999
        next if $zn != $zs;
        next if $zn - ($zn - 1) != 1;
        last;
    }

    ($BASE_LEN, $USE_INT) = $MAX_EXP_F > $MAX_EXP_I
                          ? ($MAX_EXP_F, 0) : ($MAX_EXP_I, 1);

    __PACKAGE__ -> _base_len($BASE_LEN, $USE_INT);
}

###############################################################################

sub _zero {
    # create a zero
    my $class = shift;
    return bless [ 0 ], $class;
}

sub _one {
    # create a one
    my $class = shift;
    return bless [ 1 ], $class;
}

sub _two {
    # create a two
    my $class = shift;
    return bless [ 2 ], $class;
}

sub _ten {
    # create a 10
    my $class = shift;
    my $self = $BASE_LEN == 1 ? [ 0, 1 ] : [ 10 ];
    bless $self, $class;
}

sub _1ex {
    # create a 1Ex
    my $class = shift;

    my $rem = $_[0] % $BASE_LEN;                # remainder
    my $div = ($_[0] - $rem) / $BASE_LEN;       # parts

    # With a $BASE_LEN of 6, 1e14 becomes
    # [ 000000, 000000, 100 ] -> [ 0, 0, 100 ]
    bless [ (0) x $div,  0 + ("1" . ("0" x $rem)) ], $class;
}

sub _copy {
    # make a true copy
    my $class = shift;
    return bless [ @{ $_[0] } ], $class;
}

sub import {
    my $self = shift;

    my $opts;
    my ($base_len, $use_int);
    while (@_) {
        my $param = shift;
        croak "Parameter name must be a non-empty string"
          unless defined $param && length $param;
        croak "Missing value for parameter '$param'"
          unless @_;
        my $value = shift;

        if ($param eq 'base_len' || $param eq 'use_int') {
            $opts -> {$param} = $value;
            next;
        }

        croak "Unknown parameter '$param'";
    }

    $base_len = exists $opts -> {base_len} ? $opts -> {base_len} : $BASE_LEN;
    $use_int  = exists $opts -> {use_int}  ? $opts -> {use_int}  : $USE_INT;
    __PACKAGE__ -> _base_len($base_len, $use_int);

    return $self;
}

##############################################################################
# convert back to string and number

sub _str {
    # Convert number from internal base 1eN format to string format. Internal
    # format is always normalized, i.e., no leading zeros.

    my $ary = $_[1];
    my $idx = $#$ary;           # index of last element

    if ($idx < 0) {             # should not happen
        croak("$_[1] has no elements");
    }

    # Handle first one differently, since it should not have any leading zeros.
    my $ret = int($ary->[$idx]);
    if ($idx > 0) {
        # Interestingly, the pre-padd method uses more time.
        # The old grep variant takes longer (14 vs. 10 sec).
        my $z = '0' x ($BASE_LEN - 1);
        while (--$idx >= 0) {
            $ret .= substr($z . $ary->[$idx], -$BASE_LEN);
        }
    }
    $ret;
}

sub _num {
    # Make a Perl scalar number (int/float) from a BigInt object.
    my $x = $_[1];

    return $x->[0] if @$x == 1;         # below $BASE

    # Start with the most significant element and work towards the least
    # significant element. Avoid multiplying "inf" (which happens if the number
    # overflows) with "0" (if there are zero elements in $x) since this gives
    # "nan" which propagates to the output.

    my $num = 0;
    for (my $i = $#$x ; $i >= 0 ; --$i) {
        $num *= $BASE;
        $num += $x -> [$i];
    }
    return $num;
}

##############################################################################
# actual math code

sub _add {
    # (ref to int_num_array, ref to int_num_array)
    #
    # Routine to add two base 1eX numbers stolen from Knuth Vol 2 Algorithm A
    # pg 231. There are separate routines to add and sub as per Knuth pg 233.
    # This routine modifies array x, but not y.

    my ($c, $x, $y) = @_;

    # $x + 0 => $x

    return $x if @$y == 1 && $y->[0] == 0;

    # 0 + $y => $y->copy

    if (@$x == 1 && $x->[0] == 0) {
        @$x = @$y;
        return $x;
    }

    # For each in Y, add Y to X and carry. If after that, something is left in
    # X, foreach in X add carry to X and then return X, carry. Trades one
    # "$j++" for having to shift arrays.

    my $car = 0;
    my $j = 0;
    for my $i (@$y) {
        $x->[$j] -= $BASE if $car = (($x->[$j] += $i + $car) >= $BASE) ? 1 : 0;
        $j++;
    }
    while ($car != 0) {
        $x->[$j] -= $BASE if $car = (($x->[$j] += $car) >= $BASE) ? 1 : 0;
        $j++;
    }
    $x;
}

sub _inc {
    # (ref to int_num_array, ref to int_num_array)
    # Add 1 to $x, modify $x in place
    my ($c, $x) = @_;

    for my $i (@$x) {
        return $x if ($i += 1) < $BASE; # early out
        $i = 0;                         # overflow, next
    }
    push @$x, 1 if $x->[-1] == 0;       # last overflowed, so extend
    $x;
}

sub _dec {
    # (ref to int_num_array, ref to int_num_array)
    # Sub 1 from $x, modify $x in place
    my ($c, $x) = @_;

    my $MAX = $BASE - 1;                # since MAX_VAL based on BASE
    for my $i (@$x) {
        last if ($i -= 1) >= 0;         # early out
        $i = $MAX;                      # underflow, next
    }
    pop @$x if $x->[-1] == 0 && @$x > 1; # last underflowed (but leave 0)
    $x;
}

sub _sub {
    # (ref to int_num_array, ref to int_num_array, swap)
    #
    # Subtract base 1eX numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
    # subtract Y from X by modifying x in place
    my ($c, $sx, $sy, $s) = @_;

    my $car = 0;
    my $j = 0;
    if (!$s) {
        for my $i (@$sx) {
            last unless defined $sy->[$j] || $car;
            $i += $BASE if $car = (($i -= ($sy->[$j] || 0) + $car) < 0);
            $j++;
        }
        # might leave leading zeros, so fix that
        return __strip_zeros($sx);
    }
    for my $i (@$sx) {
        # We can't do an early out if $x < $y, since we need to copy the high
        # chunks from $y. Found by Bob Mathews.
        #last unless defined $sy->[$j] || $car;
        $sy->[$j] += $BASE
          if $car = ($sy->[$j] = $i - ($sy->[$j] || 0) - $car) < 0;
        $j++;
    }
    # might leave leading zeros, so fix that
    __strip_zeros($sy);
}

sub _mul_use_int {
    # (ref to int_num_array, ref to int_num_array)
    # multiply two numbers in internal representation
    # modifies first arg, second need not be different from first
    # works for 64 bit integer with "use integer"
    my ($c, $xv, $yv) = @_;
    use integer;

    if (@$yv == 1) {
        # shortcut for two very short numbers (improved by Nathan Zook) works
        # also if xv and yv are the same reference, and handles also $x == 0
        if (@$xv == 1) {
            if (($xv->[0] *= $yv->[0]) >= $BASE) {
                $xv->[0] =
                  $xv->[0] - ($xv->[1] = $xv->[0] / $BASE) * $BASE;
            }
            return $xv;
        }
        # $x * 0 => 0
        if ($yv->[0] == 0) {
            @$xv = (0);
            return $xv;
        }

        # multiply a large number a by a single element one, so speed up
        my $y = $yv->[0];
        my $car = 0;
        foreach my $i (@$xv) {
            #$i = $i * $y + $car; $car = $i / $BASE; $i -= $car * $BASE;
            $i = $i * $y + $car;
            $i -= ($car = $i / $BASE) * $BASE;
        }
        push @$xv, $car if $car != 0;
        return $xv;
    }

    # shortcut for result $x == 0 => result = 0
    return $xv if @$xv == 1 && $xv->[0] == 0;

    # since multiplying $x with $x fails, make copy in this case
    $yv = $c->_copy($xv) if $xv == $yv;         # same references?

    my @prod = ();
    my ($prod, $car, $cty);
    for my $xi (@$xv) {
        $car = 0;
        $cty = 0;
        # looping through this if $xi == 0 is silly - so optimize it away!
        $xi = (shift(@prod) || 0), next if $xi == 0;
        for my $yi (@$yv) {
            $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
            $prod[$cty++] = $prod - ($car = $prod / $BASE) * $BASE;
        }
        $prod[$cty] += $car if $car;    # need really to check for 0?
        $xi = shift(@prod) || 0;        # || 0 makes v5.005_3 happy
    }
    push @$xv, @prod;
    $xv;
}

sub _mul_no_int {
    # (ref to int_num_array, ref to int_num_array)
    # multiply two numbers in internal representation
    # modifies first arg, second need not be different from first
    my ($c, $xv, $yv) = @_;

    if (@$yv == 1) {
        # shortcut for two very short numbers (improved by Nathan Zook) works
        # also if xv and yv are the same reference, and handles also $x == 0
        if (@$xv == 1) {
            if (($xv->[0] *= $yv->[0]) >= $BASE) {
                my $rem = $xv->[0] % $BASE;
                $xv->[1] = ($xv->[0] - $rem) / $BASE;
                $xv->[0] = $rem;
            }
            return $xv;
        }
        # $x * 0 => 0
        if ($yv->[0] == 0) {
            @$xv = (0);
            return $xv;
        }

        # multiply a large number a by a single element one, so speed up
        my $y = $yv->[0];
        my $car = 0;
        my $rem;
        foreach my $i (@$xv) {
            $i = $i * $y + $car;
            $rem = $i % $BASE;
            $car = ($i - $rem) / $BASE;
            $i = $rem;
        }
        push @$xv, $car if $car != 0;
        return $xv;
    }

    # shortcut for result $x == 0 => result = 0
    return $xv if @$xv == 1 && $xv->[0] == 0;

    # since multiplying $x with $x fails, make copy in this case
    $yv = $c->_copy($xv) if $xv == $yv;         # same references?

    my @prod = ();
    my ($prod, $rem, $car, $cty);
    for my $xi (@$xv) {
        $car = 0;
        $cty = 0;
        # looping through this if $xi == 0 is silly - so optimize it away!
        $xi = (shift(@prod) || 0), next if $xi == 0;
        for my $yi (@$yv) {
            $prod = $xi * $yi + ($prod[$cty] || 0) + $car;
            $rem = $prod % $BASE;
            $car = ($prod - $rem) / $BASE;
            $prod[$cty++] = $rem;
        }
        $prod[$cty] += $car if $car;    # need really to check for 0?
        $xi = shift(@prod) || 0;        # || 0 makes v5.005_3 happy
    }
    push @$xv, @prod;
    $xv;
}

sub _div_use_int {
    # ref to array, ref to array, modify first array and return remainder if
    # in list context

    # This version works on integers
    use integer;

    my ($c, $x, $yorg) = @_;

    # the general div algorithm here is about O(N*N) and thus quite slow, so
    # we first check for some special cases and use shortcuts to handle them.

    # if both numbers have only one element:
    if (@$x == 1 && @$yorg == 1) {
        # shortcut, $yorg and $x are two small numbers
        if (wantarray) {
            my $rem = [ $x->[0] % $yorg->[0] ];
            bless $rem, $c;
            $x->[0] = $x->[0] / $yorg->[0];
            return ($x, $rem);
        } else {
            $x->[0] = $x->[0] / $yorg->[0];
            return $x;
        }
    }

    # if x has more than one, but y has only one element:
    if (@$yorg == 1) {
        my $rem;
        $rem = $c->_mod($c->_copy($x), $yorg) if wantarray;

        # shortcut, $y is < $BASE
        my $j = @$x;
        my $r = 0;
        my $y = $yorg->[0];
        my $b;
        while ($j-- > 0) {
            $b = $r * $BASE + $x->[$j];
            $r = $b % $y;
            $x->[$j] = $b / $y;
        }
        pop(@$x) if @$x > 1 && $x->[-1] == 0;   # remove any trailing zero
        return ($x, $rem) if wantarray;
        return $x;
    }

    # now x and y have more than one element

    # check whether y has more elements than x, if so, the result is 0
    if (@$yorg > @$x) {
        my $rem;
        $rem = $c->_copy($x) if wantarray;      # make copy
        @$x = 0;                                # set to 0
        return ($x, $rem) if wantarray;         # including remainder?
        return $x;                              # only x, which is [0] now
    }

    # check whether the numbers have the same number of elements, in that case
    # the result will fit into one element and can be computed efficiently
    if (@$yorg == @$x) {
        my $cmp = 0;
        for (my $j = $#$x ; $j >= 0 ; --$j) {
            last if $cmp = $x->[$j] - $yorg->[$j];
        }

        if ($cmp == 0) {        # x = y
            @$x = 1;
            return $x, $c->_zero() if wantarray;
            return $x;
        }

        if ($cmp < 0) {         # x < y
            if (wantarray) {
                my $rem = $c->_copy($x);
                @$x = 0;
                return $x, $rem;
            }
            @$x = 0;
            return $x;
        }
    }

    # all other cases:

    my $y = $c->_copy($yorg);           # always make copy to preserve

    my $tmp;
    my $dd = $BASE / ($y->[-1] + 1);
    if ($dd != 1) {
        my $car = 0;
        for my $xi (@$x) {
            $xi = $xi * $dd + $car;
            $xi -= ($car = $xi / $BASE) * $BASE;
        }
        push(@$x, $car);
        $car = 0;
        for my $yi (@$y) {
            $yi = $yi * $dd + $car;
            $yi -= ($car = $yi / $BASE) * $BASE;
        }
    } else {
        push(@$x, 0);
    }

    # @q will accumulate the final result, $q contains the current computed
    # part of the final result

    my @q = ();
    my ($v2, $v1) = @$y[-2, -1];
    $v2 = 0 unless $v2;
    while ($#$x > $#$y) {
        my ($u2, $u1, $u0) = @$x[-3 .. -1];
        $u2 = 0 unless $u2;
        #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
        # if $v1 == 0;
        my $tmp = $u0 * $BASE + $u1;
        my $rem = $tmp % $v1;
        my $q = $u0 == $v1 ? $MAX_VAL : (($tmp - $rem) / $v1);
        --$q while $v2 * $q > ($u0 * $BASE + $u1 - $q * $v1) * $BASE + $u2;
        if ($q) {
            my $prd;
            my ($car, $bar) = (0, 0);
            for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
                $prd = $q * $y->[$yi] + $car;
                $prd -= ($car = int($prd / $BASE)) * $BASE;
                $x->[$xi] += $BASE if $bar = (($x->[$xi] -= $prd + $bar) < 0);
            }
            if ($x->[-1] < $car + $bar) {
                $car = 0;
                --$q;
                for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
                    $x->[$xi] -= $BASE
                      if $car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE);
                }
            }
        }
        pop(@$x);
        unshift(@q, $q);
    }

    if (wantarray) {
        my $d = bless [], $c;
        if ($dd != 1) {
            my $car = 0;
            my $prd;
            for my $xi (reverse @$x) {
                $prd = $car * $BASE + $xi;
                $car = $prd - ($tmp = $prd / $dd) * $dd;
                unshift @$d, $tmp;
            }
        } else {
            @$d = @$x;
        }
        @$x = @q;
        __strip_zeros($x);
        __strip_zeros($d);
        return ($x, $d);
    }
    @$x = @q;
    __strip_zeros($x);
    $x;
}

sub _div_no_int {
    # ref to array, ref to array, modify first array and return remainder if
    # in list context

    my ($c, $x, $yorg) = @_;

    # the general div algorithm here is about O(N*N) and thus quite slow, so
    # we first check for some special cases and use shortcuts to handle them.

    # if both numbers have only one element:
    if (@$x == 1 && @$yorg == 1) {
        # shortcut, $yorg and $x are two small numbers
        my $rem = [ $x->[0] % $yorg->[0] ];
        bless $rem, $c;
        $x->[0] = ($x->[0] - $rem->[0]) / $yorg->[0];
        return ($x, $rem) if wantarray;
        return $x;
    }

    # if x has more than one, but y has only one element:
    if (@$yorg == 1) {
        my $rem;
        $rem = $c->_mod($c->_copy($x), $yorg) if wantarray;

        # shortcut, $y is < $BASE
        my $j = @$x;
        my $r = 0;
        my $y = $yorg->[0];
        my $b;
        while ($j-- > 0) {
            $b = $r * $BASE + $x->[$j];
            $r = $b % $y;
            $x->[$j] = ($b - $r) / $y;
        }
        pop(@$x) if @$x > 1 && $x->[-1] == 0;   # remove any trailing zero
        return ($x, $rem) if wantarray;
        return $x;
    }

    # now x and y have more than one element

    # check whether y has more elements than x, if so, the result is 0
    if (@$yorg > @$x) {
        my $rem;
        $rem = $c->_copy($x) if wantarray;      # make copy
        @$x = 0;                                # set to 0
        return ($x, $rem) if wantarray;         # including remainder?
        return $x;                              # only x, which is [0] now
    }

    # check whether the numbers have the same number of elements, in that case
    # the result will fit into one element and can be computed efficiently
    if (@$yorg == @$x) {
        my $cmp = 0;
        for (my $j = $#$x ; $j >= 0 ; --$j) {
            last if $cmp = $x->[$j] - $yorg->[$j];
        }

        if ($cmp == 0) {        # x = y
            @$x = 1;
            return $x, $c->_zero() if wantarray;
            return $x;
        }

        if ($cmp < 0) {         # x < y
            if (wantarray) {
                my $rem = $c->_copy($x);
                @$x = 0;
                return $x, $rem;
            }
            @$x = 0;
            return $x;
        }
    }

    # all other cases:

    my $y = $c->_copy($yorg);           # always make copy to preserve

    my $tmp = $y->[-1] + 1;
    my $rem = $BASE % $tmp;
    my $dd  = ($BASE - $rem) / $tmp;
    if ($dd != 1) {
        my $car = 0;
        for my $xi (@$x) {
            $xi = $xi * $dd + $car;
            $rem = $xi % $BASE;
            $car = ($xi - $rem) / $BASE;
            $xi = $rem;
        }
        push(@$x, $car);
        $car = 0;
        for my $yi (@$y) {
            $yi = $yi * $dd + $car;
            $rem = $yi % $BASE;
            $car = ($yi - $rem) / $BASE;
            $yi = $rem;
        }
    } else {
        push(@$x, 0);
    }

    # @q will accumulate the final result, $q contains the current computed
    # part of the final result

    my @q = ();
    my ($v2, $v1) = @$y[-2, -1];
    $v2 = 0 unless $v2;
    while ($#$x > $#$y) {
        my ($u2, $u1, $u0) = @$x[-3 .. -1];
        $u2 = 0 unless $u2;
        #warn "oups v1 is 0, u0: $u0 $y->[-2] $y->[-1] l ",scalar @$y,"\n"
        # if $v1 == 0;
        my $tmp = $u0 * $BASE + $u1;
        my $rem = $tmp % $v1;
        my $q = $u0 == $v1 ? $MAX_VAL : (($tmp - $rem) / $v1);
        --$q while $v2 * $q > ($u0 * $BASE + $u1 - $q * $v1) * $BASE + $u2;
        if ($q) {
            my $prd;
            my ($car, $bar) = (0, 0);
            for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
                $prd = $q * $y->[$yi] + $car;
                $rem = $prd % $BASE;
                $car = ($prd - $rem) / $BASE;
                $prd -= $car * $BASE;
                $x->[$xi] += $BASE if $bar = (($x->[$xi] -= $prd + $bar) < 0);
            }
            if ($x->[-1] < $car + $bar) {
                $car = 0;
                --$q;
                for (my $yi = 0, my $xi = $#$x - $#$y - 1; $yi <= $#$y; ++$yi, ++$xi) {
                    $x->[$xi] -= $BASE
                      if $car = (($x->[$xi] += $y->[$yi] + $car) >= $BASE);
                }
            }
        }
        pop(@$x);
        unshift(@q, $q);
    }

    if (wantarray) {
        my $d = bless [], $c;
        if ($dd != 1) {
            my $car = 0;
            my ($prd, $rem);
            for my $xi (reverse @$x) {
                $prd = $car * $BASE + $xi;
                $rem = $prd % $dd;
                $tmp = ($prd - $rem) / $dd;
                $car = $rem;
                unshift @$d, $tmp;
            }
        } else {
            @$d = @$x;
        }
        @$x = @q;
        __strip_zeros($x);
        __strip_zeros($d);
        return ($x, $d);
    }
    @$x = @q;
    __strip_zeros($x);
    $x;
}

##############################################################################
# testing

sub _acmp {
    # Internal absolute post-normalized compare (ignore signs)
    # ref to array, ref to array, return <0, 0, >0
    # Arrays must have at least one entry; this is not checked for.
    my ($c, $cx, $cy) = @_;

    # shortcut for short numbers
    return (($cx->[0] <=> $cy->[0]) <=> 0)
      if @$cx == 1 && @$cy == 1;

    # fast comp based on number of array elements (aka pseudo-length)
    my $lxy = (@$cx - @$cy)
      # or length of first element if same number of elements (aka difference 0)
      ||
        # need int() here because sometimes the last element is '00018' vs '18'
        (length(int($cx->[-1])) - length(int($cy->[-1])));

    return -1 if $lxy < 0;      # already differs, ret
    return  1 if $lxy > 0;      # ditto

    # manual way (abort if unequal, good for early ne)
    my $a;
    my $j = @$cx;
    while (--$j >= 0) {
        last if $a = $cx->[$j] - $cy->[$j];
    }
    $a <=> 0;
}

sub _len {
    # compute number of digits in base 10

    # int() because add/sub sometimes leaves strings (like '00005') instead of
    # '5' in this place, thus causing length() to report wrong length
    my $cx = $_[1];

    (@$cx - 1) * $BASE_LEN + length(int($cx->[-1]));
}

sub _digit {
    # Return the nth digit. Zero is rightmost, so _digit(123, 0) gives 3.
    # Negative values count from the left, so _digit(123, -1) gives 1.
    my ($c, $x, $n) = @_;

    my $len = _len('', $x);

    $n += $len if $n < 0;               # -1 last, -2 second-to-last

    # Math::BigInt::Calc returns 0 if N is out of range, but this is not done
    # by the other backend libraries.

    return "0" if $n < 0 || $n >= $len; # return 0 for digits out of range

    my $elem = int($n / $BASE_LEN);     # index of array element
    my $digit = $n % $BASE_LEN;         # index of digit within the element
    substr("0" x $BASE_LEN . "$x->[$elem]", -1 - $digit, 1);
}

sub _zeros {
    # Return number of trailing zeros in decimal.
    # Check each array element for having 0 at end as long as elem == 0
    # Upon finding a elem != 0, stop.

    my $x = $_[1];

    return 0 if @$x == 1 && $x->[0] == 0;

    my $zeros = 0;
    foreach my $elem (@$x) {
        if ($elem != 0) {
            $elem =~ /[^0](0*)\z/;
            $zeros += length($1);       # count trailing zeros
            last;                       # early out
        }
        $zeros += $BASE_LEN;
    }
    $zeros;
}

##############################################################################
# _is_* routines

sub _is_zero {
    # return true if arg is zero
    @{$_[1]} == 1 && $_[1]->[0] == 0 ? 1 : 0;
}

sub _is_even {
    # return true if arg is even
    $_[1]->[0] % 2 ? 0 : 1;
}

sub _is_odd {
    # return true if arg is odd
    $_[1]->[0] % 2 ? 1 : 0;
}

sub _is_one {
    # return true if arg is one
    @{$_[1]} == 1 && $_[1]->[0] == 1 ? 1 : 0;
}

sub _is_two {
    # return true if arg is two
    @{$_[1]} == 1 && $_[1]->[0] == 2 ? 1 : 0;
}

sub _is_ten {
    # return true if arg is ten
    if ($BASE_LEN == 1) {
        @{$_[1]} == 2 && $_[1]->[0] == 0 && $_[1]->[1] == 1 ? 1 : 0;
    } else {
        @{$_[1]} == 1 && $_[1]->[0] == 10 ? 1 : 0;
    }
}

sub __strip_zeros {
    # Internal normalization function that strips leading zeros from the array.
    # Args: ref to array
    my $x = shift;

    push @$x, 0 if @$x == 0;    # div might return empty results, so fix it
    return $x if @$x == 1;      # early out

    #print "strip: cnt $cnt i $i\n";
    # '0', '3', '4', '0', '0',
    #  0    1    2    3    4
    # cnt = 5, i = 4
    # i = 4
    # i = 3
    # => fcnt = cnt - i (5-2 => 3, cnt => 5-1 = 4, throw away from 4th pos)
    # >= 1: skip first part (this can be zero)

    my $i = $#$x;
    while ($i > 0) {
        last if $x->[$i] != 0;
        $i--;
    }
    $i++;
    splice(@$x, $i) if $i < @$x;
    $x;
}

###############################################################################
# check routine to test internal state for corruptions

sub _check {
    # used by the test suite
    my ($class, $x) = @_;

    my $msg = $class -> SUPER::_check($x);
    return $msg if $msg;

    my $n;
    eval { $n = @$x };
    return "Not an array reference" unless $@ eq '';

    return "Reference to an empty array" unless $n > 0;

    # The following fails with Math::BigInt::FastCalc because a
    # Math::BigInt::FastCalc "object" is an unblessed array ref.
    #
    #return 0 unless ref($x) eq $class;

    for (my $i = 0 ; $i <= $#$x ; ++ $i) {
        my $e = $x -> [$i];

        return "Element at index $i is undefined"
          unless defined $e;

        return "Element at index $i is a '" . ref($e) .
          "', which is not a scalar"
          unless ref($e) eq "";

        # It would be better to use the regex /^([1-9]\d*|0)\z/, but that fails
        # in Math::BigInt::FastCalc, because it sometimes creates array
        # elements like "000000".
        return "Element at index $i is '$e', which does not look like an" .
          " normal integer" unless $e =~ /^\d+\z/;

        return "Element at index $i is '$e', which is not smaller than" .
          " the base '$BASE'" if $e >= $BASE;

        return "Element at index $i (last element) is zero"
          if $#$x > 0 && $i == $#$x && $e == 0;
    }

    return 0;
}

###############################################################################

sub _mod {
    # if possible, use mod shortcut
    my ($c, $x, $yo) = @_;

    # slow way since $y too big
    if (@$yo > 1) {
        my ($xo, $rem) = $c->_div($x, $yo);
        @$x = @$rem;
        return $x;
    }

    my $y = $yo->[0];

    # if both are single element arrays
    if (@$x == 1) {
        $x->[0] %= $y;
        return $x;
    }

    # if @$x has more than one element, but @$y is a single element
    my $b = $BASE % $y;
    if ($b == 0) {
        # when BASE % Y == 0 then (B * BASE) % Y == 0
        # (B * BASE) % $y + A % Y => A % Y
        # so need to consider only last element: O(1)
        $x->[0] %= $y;
    } elsif ($b == 1) {
        # else need to go through all elements in @$x: O(N), but loop is a bit
        # simplified
        my $r = 0;
        foreach (@$x) {
            $r = ($r + $_) % $y; # not much faster, but heh...
            #$r += $_ % $y; $r %= $y;
        }
        $r = 0 if $r == $y;
        $x->[0] = $r;
    } else {
        # else need to go through all elements in @$x: O(N)
        my $r = 0;
        my $bm = 1;
        foreach (@$x) {
            $r = ($_ * $bm + $r) % $y;
            $bm = ($bm * $b) % $y;

            #$r += ($_ % $y) * $bm;
            #$bm *= $b;
            #$bm %= $y;
            #$r %= $y;
        }
        $r = 0 if $r == $y;
        $x->[0] = $r;
    }
    @$x = $x->[0];              # keep one element of @$x
    return $x;
}

##############################################################################
# shifts

sub _rsft {
    my ($c, $x, $n, $b) = @_;
    return $x if $c->_is_zero($x) || $c->_is_zero($n);

    # For backwards compatibility, allow the base $b to be a scalar.

    $b = $c->_new($b) unless ref $b;

    if ($c -> _acmp($b, $c -> _ten())) {
        return scalar $c->_div($x, $c->_pow($c->_copy($b), $n));
    }

    # shortcut (faster) for shifting by 10)
    # multiples of $BASE_LEN
    my $dst = 0;                # destination
    my $src = $c->_num($n);     # as normal int
    my $xlen = (@$x - 1) * $BASE_LEN + length(int($x->[-1]));
    if ($src >= $xlen or ($src == $xlen and !defined $x->[1])) {
        # 12345 67890 shifted right by more than 10 digits => 0
        splice(@$x, 1);         # leave only one element
        $x->[0] = 0;            # set to zero
        return $x;
    }
    my $rem = $src % $BASE_LEN;   # remainder to shift
    $src = int($src / $BASE_LEN); # source
    if ($rem == 0) {
        splice(@$x, 0, $src);   # even faster, 38.4 => 39.3
    } else {
        my $len = @$x - $src;   # elems to go
        my $vd;
        my $z = '0' x $BASE_LEN;
        $x->[ @$x ] = 0;          # avoid || 0 test inside loop
        while ($dst < $len) {
            $vd = $z . $x->[$src];
            $vd = substr($vd, -$BASE_LEN, $BASE_LEN - $rem);
            $src++;
            $vd = substr($z . $x->[$src], -$rem, $rem) . $vd;
            $vd = substr($vd, -$BASE_LEN, $BASE_LEN) if length($vd) > $BASE_LEN;
            $x->[$dst] = int($vd);
            $dst++;
        }
        splice(@$x, $dst) if $dst > 0;       # kill left-over array elems
        pop(@$x) if $x->[-1] == 0 && @$x > 1; # kill last element if 0
    }                                        # else rem == 0
    $x;
}

sub _lsft {
    my ($c, $x, $n, $b) = @_;

    return $x if $c->_is_zero($x) || $c->_is_zero($n);

    # For backwards compatibility, allow the base $b to be a scalar.

    $b = $c->_new($b) unless ref $b;

    # If the base is a power of 10, use shifting, since the internal
    # representation is in base 10eX.

    my $bstr = $c->_str($b);
    if ($bstr =~ /^1(0+)\z/) {

        # Adjust $n so that we're shifting in base 10. Do this by multiplying
        # $n by the base 10 logarithm of $b: $b ** $n = 10 ** (log10($b) * $n).

        my $log10b = length($1);
        $n = $c->_mul($c->_new($log10b), $n);
        $n = $c->_num($n);              # shift-len as normal int

        # $q is the number of places to shift the elements within the array,
        # and $r is the number of places to shift the values within the
        # elements.

        my $r = $n % $BASE_LEN;
        my $q = ($n - $r) / $BASE_LEN;

        # If we must shift the values within the elements ...

        if ($r) {
            my $i = @$x;                # index
            $x->[$i] = 0;               # initialize most significant element
            my $z = '0' x $BASE_LEN;
            my $vd;
            while ($i >= 0) {
                $vd = $x->[$i];
                $vd = $z . $vd;
                $vd = substr($vd, $r - $BASE_LEN, $BASE_LEN - $r);
                $vd .= $i > 0 ? substr($z . $x->[$i - 1], -$BASE_LEN, $r)
                              : '0' x $r;
                $vd = substr($vd, -$BASE_LEN, $BASE_LEN) if length($vd) > $BASE_LEN;
                $x->[$i] = int($vd);    # e.g., "0...048" -> 48 etc.
                $i--;
            }

            pop(@$x) if $x->[-1] == 0;  # if most significant element is zero
        }

        # If we must shift the elements within the array ...

        if ($q) {
            unshift @$x, (0) x $q;
        }

    } else {
        $x = $c->_mul($x, $c->_pow($b, $n));
    }

    return $x;
}

sub _pow {
    # power of $x to $y
    # ref to array, ref to array, return ref to array
    my ($c, $cx, $cy) = @_;

    if (@$cy == 1 && $cy->[0] == 0) {
        splice(@$cx, 1);
        $cx->[0] = 1;           # y == 0 => x => 1
        return $cx;
    }

    if ((@$cx == 1 && $cx->[0] == 1) || #    x == 1
        (@$cy == 1 && $cy->[0] == 1))   # or y == 1
    {
        return $cx;
    }

    if (@$cx == 1 && $cx->[0] == 0) {
        splice (@$cx, 1);
        $cx->[0] = 0;           # 0 ** y => 0 (if not y <= 0)
        return $cx;
    }

    my $pow2 = $c->_one();

    my $y_bin = $c->_as_bin($cy);
    $y_bin =~ s/^0b//;
    my $len = length($y_bin);
    while (--$len > 0) {
        $c->_mul($pow2, $cx) if substr($y_bin, $len, 1) eq '1'; # is odd?
        $c->_mul($cx, $cx);
    }

    $c->_mul($cx, $pow2);
    $cx;
}

sub _nok {
    # Return binomial coefficient (n over k).
    # Given refs to arrays, return ref to array.
    # First input argument is modified.

    my ($c, $n, $k) = @_;

    # If k > n/2, or, equivalently, 2*k > n, compute nok(n, k) as
    # nok(n, n-k), to minimize the number if iterations in the loop.

    {
        my $twok = $c->_mul($c->_two(), $c->_copy($k)); # 2 * k
        if ($c->_acmp($twok, $n) > 0) {               # if 2*k > n
            $k = $c->_sub($c->_copy($n), $k);         # k = n - k
        }
    }

    # Example:
    #
    # / 7 \       7!       1*2*3*4 * 5*6*7   5 * 6 * 7       6   7
    # |   | = --------- =  --------------- = --------- = 5 * - * -
    # \ 3 /   (7-3)! 3!    1*2*3*4 * 1*2*3   1 * 2 * 3       2   3

    if ($c->_is_zero($k)) {
        @$n = 1;
    } else {

        # Make a copy of the original n, since we'll be modifying n in-place.

        my $n_orig = $c->_copy($n);

        # n = 5, f = 6, d = 2 (cf. example above)

        $c->_sub($n, $k);
        $c->_inc($n);

        my $f = $c->_copy($n);
        $c->_inc($f);

        my $d = $c->_two();

        # while f <= n (the original n, that is) ...

        while ($c->_acmp($f, $n_orig) <= 0) {

            # n = (n * f / d) == 5 * 6 / 2 (cf. example above)

            $c->_mul($n, $f);
            $c->_div($n, $d);

            # f = 7, d = 3 (cf. example above)

            $c->_inc($f);
            $c->_inc($d);
        }

    }

    return $n;
}

sub _fac {
    # factorial of $x
    # ref to array, return ref to array
    my ($c, $cx) = @_;

    # We cache the smallest values. Don't assume that a single element has a
    # value larger than 9 or else it won't work with a $BASE_LEN of 1.

    if (@$cx == 1) {
        my @factorials =
          (
           '1',
           '1',
           '2',
           '6',
           '24',
           '120',
           '720',
           '5040',
           '40320',
           '362880',
          );
        if ($cx->[0] <= $#factorials) {
            my $tmp = $c -> _new($factorials[ $cx->[0] ]);
            @$cx = @$tmp;
            return $cx;
        }
    }

    # The old code further below doesn't work for small values of $BASE_LEN.
    # Alas, I have not been able to (or taken the time to) decipher it, so for
    # the case when $BASE_LEN is small, we call the parent class. This code
    # works in for every value of $x and $BASE_LEN. We could use this code for
    # all cases, but it is a little slower than the code further below, so at
    # least for now we keep the code below.

    if ($BASE_LEN <= 2) {
        my $tmp = $c -> SUPER::_fac($cx);
        @$cx = @$tmp;
        return $cx;
    }

    # This code does not work for small values of $BASE_LEN.

    if ((@$cx == 1) &&          # we do this only if $x >= 12 and $x <= 7000
        ($cx->[0] >= 12 && $cx->[0] < 7000)) {

        # Calculate (k-j) * (k-j+1) ... k .. (k+j-1) * (k + j)
        # See http://blogten.blogspot.com/2007/01/calculating-n.html
        # The above series can be expressed as factors:
        #   k * k - (j - i) * 2
        # We cache k*k, and calculate (j * j) as the sum of the first j odd integers

        # This will not work when N exceeds the storage of a Perl scalar, however,
        # in this case the algorithm would be way too slow to terminate, anyway.

        # As soon as the last element of $cx is 0, we split it up and remember
        # how many zeors we got so far. The reason is that n! will accumulate
        # zeros at the end rather fast.
        my $zero_elements = 0;

        # If n is even, set n = n -1
        my $k = $c->_num($cx);
        my $even = 1;
        if (($k & 1) == 0) {
            $even = $k;
            $k --;
        }
        # set k to the center point
        $k = ($k + 1) / 2;
        #  print "k $k even: $even\n";
        # now calculate k * k
        my $k2 = $k * $k;
        my $odd = 1;
        my $sum = 1;
        my $i = $k - 1;
        # keep reference to x
        my $new_x = $c->_new($k * $even);
        @$cx = @$new_x;
        if ($cx->[0] == 0) {
            $zero_elements ++;
            shift @$cx;
        }
        #  print STDERR "x = ", $c->_str($cx), "\n";
        my $BASE2 = int(sqrt($BASE))-1;
        my $j = 1;
        while ($j <= $i) {
            my $m = ($k2 - $sum);
            $odd += 2;
            $sum += $odd;
            $j++;
            while ($j <= $i && ($m < $BASE2) && (($k2 - $sum) < $BASE2)) {
                $m *= ($k2 - $sum);
                $odd += 2;
                $sum += $odd;
                $j++;
                #      print STDERR "\n k2 $k2 m $m sum $sum odd $odd\n"; sleep(1);
            }
            if ($m < $BASE) {
                $c->_mul($cx, [$m]);
            } else {
                $c->_mul($cx, $c->_new($m));
            }
            if ($cx->[0] == 0) {
                $zero_elements ++;
                shift @$cx;
            }
            #    print STDERR "Calculate $k2 - $sum = $m (x = ", $c->_str($cx), ")\n";
        }
        # multiply in the zeros again
        unshift @$cx, (0) x $zero_elements;
        return $cx;
    }

    # go forward until $base is exceeded limit is either $x steps (steps == 100
    # means a result always too high) or $base.
    my $steps = 100;
    $steps = $cx->[0] if @$cx == 1;
    my $r = 2;
    my $cf = 3;
    my $step = 2;
    my $last = $r;
    while ($r * $cf < $BASE && $step < $steps) {
        $last = $r;
        $r *= $cf++;
        $step++;
    }
    if ((@$cx == 1) && $step == $cx->[0]) {
        # completely done, so keep reference to $x and return
        $cx->[0] = $r;
        return $cx;
    }

    # now we must do the left over steps
    my $n;                      # steps still to do
    if (@$cx == 1) {
        $n = $cx->[0];
    } else {
        $n = $c->_copy($cx);
    }

    # Set $cx to the last result below $BASE (but keep ref to $x)
    $cx->[0] = $last;
    splice (@$cx, 1);
    # As soon as the last element of $cx is 0, we split it up and remember
    # how many zeors we got so far. The reason is that n! will accumulate
    # zeros at the end rather fast.
    my $zero_elements = 0;

    # do left-over steps fit into a scalar?
    if (ref $n eq 'ARRAY') {
        # No, so use slower inc() & cmp()
        # ($n is at least $BASE here)
        my $base_2 = int(sqrt($BASE)) - 1;
        #print STDERR "base_2: $base_2\n";
        while ($step < $base_2) {
            if ($cx->[0] == 0) {
                $zero_elements ++;
                shift @$cx;
            }
            my $b = $step * ($step + 1);
            $step += 2;
            $c->_mul($cx, [$b]);
        }
        $step = [$step];
        while ($c->_acmp($step, $n) <= 0) {
            if ($cx->[0] == 0) {
                $zero_elements ++;
                shift @$cx;
            }
            $c->_mul($cx, $step);
            $c->_inc($step);
        }
    } else {
        # Yes, so we can speed it up slightly

        #    print "# left over steps $n\n";

        my $base_4 = int(sqrt(sqrt($BASE))) - 2;
        #print STDERR "base_4: $base_4\n";
        my $n4 = $n - 4;
        while ($step < $n4 && $step < $base_4) {
            if ($cx->[0] == 0) {
                $zero_elements ++;
                shift @$cx;
            }
            my $b = $step * ($step + 1);
            $step += 2;
            $b *= $step * ($step + 1);
            $step += 2;
            $c->_mul($cx, [$b]);
        }
        my $base_2 = int(sqrt($BASE)) - 1;
        my $n2 = $n - 2;
        #print STDERR "base_2: $base_2\n";
        while ($step < $n2 && $step < $base_2) {
            if ($cx->[0] == 0) {
                $zero_elements ++;
                shift @$cx;
            }
            my $b = $step * ($step + 1);
            $step += 2;
            $c->_mul($cx, [$b]);
        }
        # do what's left over
        while ($step <= $n) {
            $c->_mul($cx, [$step]);
            $step++;
            if ($cx->[0] == 0) {
                $zero_elements ++;
                shift @$cx;
            }
        }
    }
    # multiply in the zeros again
    unshift @$cx, (0) x $zero_elements;
    $cx;                        # return result
}

sub _log_int {
    # calculate integer log of $x to base $base
    # ref to array, ref to array - return ref to array
    my ($c, $x, $base) = @_;

    # X == 0 => NaN
    return if @$x == 1 && $x->[0] == 0;

    # BASE 0 or 1 => NaN
    return if @$base == 1 && $base->[0] < 2;

    # X == 1 => 0 (is exact)
    if (@$x == 1 && $x->[0] == 1) {
        @$x = 0;
        return $x, 1;
    }

    my $cmp = $c->_acmp($x, $base);

    # X == BASE => 1 (is exact)
    if ($cmp == 0) {
        @$x = 1;
        return $x, 1;
    }

    # 1 < X < BASE => 0 (is truncated)
    if ($cmp < 0) {
        @$x = 0;
        return $x, 0;
    }

    my $x_org = $c->_copy($x);  # preserve x

    # Compute a guess for the result based on:
    # $guess = int ( length_in_base_10(X) / ( log(base) / log(10) ) )
    my $len = $c->_len($x_org);
    my $log = log($base->[-1]) / log(10);

    # for each additional element in $base, we add $BASE_LEN to the result,
    # based on the observation that log($BASE, 10) is BASE_LEN and
    # log(x*y) == log(x) + log(y):
    $log += (@$base - 1) * $BASE_LEN;

    # calculate now a guess based on the values obtained above:
    my $res = $c->_new(int($len / $log));

    @$x = @$res;
    my $trial = $c->_pow($c->_copy($base), $x);
    my $acmp = $c->_acmp($trial, $x_org);

    # Did we get the exact result?

    return $x, 1 if $acmp == 0;

    # Too small?

    while ($acmp < 0) {
        $c->_mul($trial, $base);
        $c->_inc($x);
        $acmp = $c->_acmp($trial, $x_org);
    }

    # Too big?

    while ($acmp > 0) {
        $c->_div($trial, $base);
        $c->_dec($x);
        $acmp = $c->_acmp($trial, $x_org);
    }

    return $x, 1 if $acmp == 0;         # result is exact
    return $x, 0;                       # result is too small
}

# for debugging:
use constant DEBUG => 0;
my $steps = 0;
sub steps { $steps };

sub _sqrt {
    # square-root of $x in-place

    my ($c, $x) = @_;

    if (@$x == 1) {
        # fits into one Perl scalar, so result can be computed directly
        $x->[0] = int(sqrt($x->[0]));
        return $x;
    }

    # Create an initial guess for the square root.

    my $s;
    if (@$x % 2) {
        $s = [ (0) x ((@$x - 1) / 2), int(sqrt($x->[-1])) ];
    } else {
        $s = [ (0) x ((@$x - 2) / 2), int(sqrt($x->[-2] + $x->[-1] * $BASE)) ];
    }

    # Newton's method for the square root of y:
    #
    #                      x(n) * x(n) - y
    #     x(n+1) = x(n) - -----------------
    #                          2 * x(n)

    my $cmp;
    while (1) {
        my $sq = $c -> _mul($c -> _copy($s), $s);
        $cmp = $c -> _acmp($sq, $x);

        # If x(n)*x(n) > y, compute
        #
        #                      x(n) * x(n) - y
        #     x(n+1) = x(n) - -----------------
        #                          2 * x(n)

        if ($cmp > 0) {
            my $num = $c -> _sub($c -> _copy($sq), $x);
            my $den = $c -> _mul($c -> _two(), $s);
            my $delta = $c -> _div($num, $den);
            last if $c -> _is_zero($delta);
            $s = $c -> _sub($s, $delta);
        }

        # If x(n)*x(n) < y, compute
        #
        #                      y - x(n) * x(n)
        #     x(n+1) = x(n) + -----------------
        #                          2 * x(n)

        elsif ($cmp < 0) {
            my $num = $c -> _sub($c -> _copy($x), $sq);
            my $den = $c -> _mul($c -> _two(), $s);
            my $delta = $c -> _div($num, $den);
            last if $c -> _is_zero($delta);
            $s = $c -> _add($s, $delta);
        }

        # If x(n)*x(n) = y, we have the exact result.

        else {
            last;
        }
    }

    $s = $c -> _dec($s) if $cmp > 0;    # never overshoot
    @$x = @$s;
    return $x;
}

sub _root {
    # Take n'th root of $x in place.

    my ($c, $x, $n) = @_;

    # Small numbers.

    if (@$x == 1) {
        return $x if $x -> [0] == 0 || $x -> [0] == 1;

        if (@$n == 1) {
            # Result can be computed directly. Adjust initial result for
            # numerical errors, e.g., int(1000**(1/3)) is 2, not 3.
            my $y = int($x->[0] ** (1 / $n->[0]));
            my $yp1 = $y + 1;
            $y = $yp1 if $yp1 ** $n->[0] == $x->[0];
            $x->[0] = $y;
            return $x;
        }
    }

    # If x <= n, the result is always (truncated to) 1.

    if ((@$x > 1 || $x -> [0] > 0) &&           # if x is non-zero ...
        $c -> _acmp($x, $n) <= 0)               # ... and x <= n
    {
        my $one = $c -> _one();
        @$x = @$one;
        return $x;
    }

    # If $n is a power of two, take sqrt($x) repeatedly, e.g., root($x, 4) =
    # sqrt(sqrt($x)), root($x, 8) = sqrt(sqrt(sqrt($x))).

    my $b = $c -> _as_bin($n);
    if ($b =~ /0b1(0+)$/) {
        my $count = length($1);       # 0b100 => len('00') => 2
        my $cnt = $count;             # counter for loop
        unshift @$x, 0;               # add one element, together with one
                                      #   more below in the loop this makes 2
        while ($cnt-- > 0) {
            # 'Inflate' $x by adding one element, basically computing
            # $x * $BASE * $BASE. This gives us more $BASE_LEN digits for
            # result since len(sqrt($X)) approx == len($x) / 2.
            unshift @$x, 0;
            # Calculate sqrt($x), $x is now one element to big, again. In the
            # next round we make that two, again.
            $c -> _sqrt($x);
        }

        # $x is now one element too big, so truncate result by removing it.
        shift @$x;

        return $x;
    }

    my $DEBUG = 0;

    # Now the general case. This works by finding an initial guess. If this
    # guess is incorrect, a relatively small delta is chosen. This delta is
    # used to find a lower and upper limit for the correct value. The delta is
    # doubled in each iteration. When a lower and upper limit is found,
    # bisection is applied to narrow down the region until we have the correct
    # value.

    # Split x into mantissa and exponent in base 10, so that
    #
    #   x = xm * 10^xe, where 0 < xm < 1 and xe is an integer

    my $x_str = $c -> _str($x);
    my $xm    = "." . $x_str;
    my $xe    = length($x_str);

    # From this we compute the base 10 logarithm of x
    #
    #   log_10(x) = log_10(xm) + log_10(xe^10)
    #             = log(xm)/log(10) + xe
    #
    # and then the base 10 logarithm of y, where y = x^(1/n)
    #
    #   log_10(y) = log_10(x)/n

    my $log10x = log($xm) / log(10) + $xe;
    my $log10y = $log10x / $c -> _num($n);

    # And from this we compute ym and ye, the mantissa and exponent (in
    # base 10) of y, where 1 < ym <= 10 and ye is an integer.

    my $ye = int $log10y;
    my $ym = 10 ** ($log10y - $ye);

    # Finally, we scale the mantissa and exponent to incraese the integer
    # part of ym, before building the string representing our guess of y.

    if ($DEBUG) {
        print "\n";
        print "xm     = $xm\n";
        print "xe     = $xe\n";
        print "log10x = $log10x\n";
        print "log10y = $log10y\n";
        print "ym     = $ym\n";
        print "ye     = $ye\n";
        print "\n";
    }

    my $d = $ye < 15 ? $ye : 15;
    $ym *= 10 ** $d;
    $ye -= $d;

    my $y_str = sprintf('%.0f', $ym) . "0" x $ye;
    my $y = $c -> _new($y_str);

    if ($DEBUG) {
        print "ym     = $ym\n";
        print "ye     = $ye\n";
        print "\n";
        print "y_str  = $y_str (initial guess)\n";
        print "\n";
    }

    # See if our guess y is correct.

    my $trial = $c -> _pow($c -> _copy($y), $n);
    my $acmp  = $c -> _acmp($trial, $x);

    if ($acmp == 0) {
        @$x = @$y;
        return $x;
    }

    # Find a lower and upper limit for the correct value of y. Start off with a
    # delta value that is approximately the size of the accuracy of the guess.

    my $lower;
    my $upper;

    my $delta = $c -> _new("1" . ("0" x $ye));
    my $two   = $c -> _two();

    if ($acmp < 0) {
        $lower = $y;
        while ($acmp < 0) {
            $upper = $c -> _add($c -> _copy($lower), $delta);

            if ($DEBUG) {
                print "lower  = $lower\n";
                print "upper  = $upper\n";
                print "delta  = $delta\n";
                print "\n";
            }
            $acmp  = $c -> _acmp($c -> _pow($c -> _copy($upper), $n), $x);
            if ($acmp == 0) {
                @$x = @$upper;
                return $x;
            }
            $delta = $c -> _mul($delta, $two);
        }
    }

    elsif ($acmp > 0) {
        $upper = $y;
        while ($acmp > 0) {
            if ($c -> _acmp($upper, $delta) <= 0) {
                $lower = $c -> _zero();
                last;
            }
            $lower = $c -> _sub($c -> _copy($upper), $delta);

            if ($DEBUG) {
                print "lower  = $lower\n";
                print "upper  = $upper\n";
                print "delta  = $delta\n";
                print "\n";
            }
            $acmp  = $c -> _acmp($c -> _pow($c -> _copy($lower), $n), $x);
            if ($acmp == 0) {
                @$x = @$lower;
                return $x;
            }
            $delta = $c -> _mul($delta, $two);
        }
    }

    # Use bisection to narrow down the interval.

    my $one = $c -> _one();
    {

        $delta = $c -> _sub($c -> _copy($upper), $lower);
        if ($c -> _acmp($delta, $one) <= 0) {
            @$x = @$lower;
            return $x;
        }

        if ($DEBUG) {
            print "lower  = $lower\n";
            print "upper  = $upper\n";
            print "delta   = $delta\n";
            print "\n";
        }

        $delta = $c -> _div($delta, $two);
        my $middle = $c -> _add($c -> _copy($lower), $delta);

        $acmp  = $c -> _acmp($c -> _pow($c -> _copy($middle), $n), $x);
        if ($acmp < 0) {
            $lower = $middle;
        } elsif ($acmp > 0) {
            $upper = $middle;
        } else {
            @$x = @$middle;
            return $x;
        }

        redo;
    }

    $x;
}

##############################################################################
# binary stuff

sub _and {
    my ($c, $x, $y) = @_;

    # the shortcut makes equal, large numbers _really_ fast, and makes only a
    # very small performance drop for small numbers (e.g. something with less
    # than 32 bit) Since we optimize for large numbers, this is enabled.
    return $x if $c->_acmp($x, $y) == 0; # shortcut

    my $m = $c->_one();
    my ($xr, $yr);
    my $mask = $AND_MASK;

    my $x1 = $c->_copy($x);
    my $y1 = $c->_copy($y);
    my $z  = $c->_zero();

    use integer;
    until ($c->_is_zero($x1) || $c->_is_zero($y1)) {
        ($x1, $xr) = $c->_div($x1, $mask);
        ($y1, $yr) = $c->_div($y1, $mask);

        $c->_add($z, $c->_mul([ 0 + $xr->[0] & 0 + $yr->[0] ], $m));
        $c->_mul($m, $mask);
    }

    @$x = @$z;
    return $x;
}

sub _xor {
    my ($c, $x, $y) = @_;

    return $c->_zero() if $c->_acmp($x, $y) == 0; # shortcut (see -and)

    my $m = $c->_one();
    my ($xr, $yr);
    my $mask = $XOR_MASK;

    my $x1 = $c->_copy($x);
    my $y1 = $c->_copy($y);      # make copy
    my $z  = $c->_zero();

    use integer;
    until ($c->_is_zero($x1) || $c->_is_zero($y1)) {
        ($x1, $xr) = $c->_div($x1, $mask);
        ($y1, $yr) = $c->_div($y1, $mask);
        # make ints() from $xr, $yr (see _and())
        #$b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
        #$b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
        #$c->_add($x, $c->_mul($c->_new($xrr ^ $yrr)), $m) );

        $c->_add($z, $c->_mul([ 0 + $xr->[0] ^ 0 + $yr->[0] ], $m));
        $c->_mul($m, $mask);
    }
    # the loop stops when the shorter of the two numbers is exhausted
    # the remainder of the longer one will survive bit-by-bit, so we simple
    # multiply-add it in
    $c->_add($z, $c->_mul($x1, $m) ) if !$c->_is_zero($x1);
    $c->_add($z, $c->_mul($y1, $m) ) if !$c->_is_zero($y1);

    @$x = @$z;
    return $x;
}

sub _or {
    my ($c, $x, $y) = @_;

    return $x if $c->_acmp($x, $y) == 0; # shortcut (see _and)

    my $m = $c->_one();
    my ($xr, $yr);
    my $mask = $OR_MASK;

    my $x1 = $c->_copy($x);
    my $y1 = $c->_copy($y);      # make copy
    my $z  = $c->_zero();

    use integer;
    until ($c->_is_zero($x1) || $c->_is_zero($y1)) {
        ($x1, $xr) = $c->_div($x1, $mask);
        ($y1, $yr) = $c->_div($y1, $mask);
        # make ints() from $xr, $yr (see _and())
        #    $b = 1; $xrr = 0; foreach (@$xr) { $xrr += $_ * $b; $b *= $BASE; }
        #    $b = 1; $yrr = 0; foreach (@$yr) { $yrr += $_ * $b; $b *= $BASE; }
        #    $c->_add($x, $c->_mul(_new( $c, ($xrr | $yrr) ), $m) );
        $c->_add($z, $c->_mul([ 0 + $xr->[0] | 0 + $yr->[0] ], $m));
        $c->_mul($m, $mask);
    }
    # the loop stops when the shorter of the two numbers is exhausted
    # the remainder of the longer one will survive bit-by-bit, so we simple
    # multiply-add it in
    $c->_add($z, $c->_mul($x1, $m) ) if !$c->_is_zero($x1);
    $c->_add($z, $c->_mul($y1, $m) ) if !$c->_is_zero($y1);

    @$x = @$z;
    return $x;
}

sub _as_hex {
    # convert a decimal number to hex (ref to array, return ref to string)
    my ($c, $x) = @_;

    return "0x0" if @$x == 1 && $x->[0] == 0;

    my $x1 = $c->_copy($x);

    my $x10000 = [ 0x10000 ];

    my $es = '';
    my $xr;
    until (@$x1 == 1 && $x1->[0] == 0) {        # _is_zero()
        ($x1, $xr) = $c->_div($x1, $x10000);
        $es = sprintf('%04x', $xr->[0]) . $es;
    }
    #$es = reverse $es;
    $es =~ s/^0*/0x/;
    return $es;
}

sub _as_bin {
    # convert a decimal number to bin (ref to array, return ref to string)
    my ($c, $x) = @_;

    return "0b0" if @$x == 1 && $x->[0] == 0;

    my $x1 = $c->_copy($x);

    my $x10000 = [ 0x10000 ];

    my $es = '';
    my $xr;

    until (@$x1 == 1 && $x1->[0] == 0) {        # _is_zero()
        ($x1, $xr) = $c->_div($x1, $x10000);
        $es = sprintf('%016b', $xr->[0]) . $es;
    }
    $es =~ s/^0*/0b/;
    return $es;
}

sub _as_oct {
    # convert a decimal number to octal (ref to array, return ref to string)
    my ($c, $x) = @_;

    return "00" if @$x == 1 && $x->[0] == 0;

    my $x1 = $c->_copy($x);

    my $x1000 = [ 1 << 15 ];    # 15 bits = 32768 = 0100000

    my $es = '';
    my $xr;
    until (@$x1 == 1 && $x1->[0] == 0) {        # _is_zero()
        ($x1, $xr) = $c->_div($x1, $x1000);
        $es = sprintf("%05o", $xr->[0]) . $es;
    }
    $es =~ s/^0*/0/;            # excactly one leading zero
    return $es;
}

sub _from_oct {
    # convert a octal number to decimal (string, return ref to array)
    my ($c, $os) = @_;

    my $m = $c->_new(1 << 30);          # 30 bits at a time (<32 bits!)
    my $d = 10;                         # 10 octal digits at a time

    my $mul = $c->_one();
    my $x = $c->_zero();

    my $len = int((length($os) - 1) / $d);      # $d digit parts, w/o the '0'
    my $val;
    my $i = -$d;
    while ($len >= 0) {
        $val = substr($os, $i, $d);             # get oct digits
        $val = CORE::oct($val);
        $i -= $d;
        $len --;
        my $adder = $c -> _new($val);
        $c->_add($x, $c->_mul($adder, $mul)) if $val != 0;
        $c->_mul($mul, $m) if $len >= 0;        # skip last mul
    }
    $x;
}

sub _from_hex {
    # convert a hex number to decimal (string, return ref to array)
    my ($c, $hs) = @_;

    my $m = $c->_new(0x10000000);       # 28 bit at a time (<32 bit!)
    my $d = 7;                          # 7 hexadecimal digits at a time
    my $mul = $c->_one();
    my $x = $c->_zero();

    my $len = int((length($hs) - 2) / $d); # $d digit parts, w/o the '0x'
    my $val;
    my $i = -$d;
    while ($len >= 0) {
        $val = substr($hs, $i, $d);     # get hex digits
        $val =~ s/^0x// if $len == 0; # for last part only because
        $val = CORE::hex($val);       # hex does not like wrong chars
        $i -= $d;
        $len --;
        my $adder = $c->_new($val);
        # if the resulting number was to big to fit into one element, create a
        # two-element version (bug found by Mark Lakata - Thanx!)
        if (CORE::length($val) > $BASE_LEN) {
            $adder = $c->_new($val);
        }
        $c->_add($x, $c->_mul($adder, $mul)) if $val != 0;
        $c->_mul($mul, $m) if $len >= 0; # skip last mul
    }
    $x;
}

sub _from_bin {
    # convert a hex number to decimal (string, return ref to array)
    my ($c, $bs) = @_;

    # instead of converting X (8) bit at a time, it is faster to "convert" the
    # number to hex, and then call _from_hex.

    my $hs = $bs;
    $hs =~ s/^[+-]?0b//;                                # remove sign and 0b
    my $l = length($hs);                                # bits
    $hs = '0' x (8 - ($l % 8)) . $hs if ($l % 8) != 0;  # padd left side w/ 0
    my $h = '0x' . unpack('H*', pack ('B*', $hs));      # repack as hex

    $c->_from_hex($h);
}

##############################################################################
# special modulus functions

sub _modinv {

    # modular multiplicative inverse
    my ($c, $x, $y) = @_;

    # modulo zero
    if ($c->_is_zero($y)) {
        return;
    }

    # modulo one
    if ($c->_is_one($y)) {
        return $c->_zero(), '+';
    }

    my $u = $c->_zero();
    my $v = $c->_one();
    my $a = $c->_copy($y);
    my $b = $c->_copy($x);

    # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the result
    # ($u) at the same time. See comments in BigInt for why this works.
    my $q;
    my $sign = 1;
    {
        ($a, $q, $b) = ($b, $c->_div($a, $b));          # step 1
        last if $c->_is_zero($b);

        my $t = $c->_add(                               # step 2:
                         $c->_mul($c->_copy($v), $q),   #  t =   v * q
                         $u);                           #      + u
        $u = $v;                                        #  u = v
        $v = $t;                                        #  v = t
        $sign = -$sign;
        redo;
    }

    # if the gcd is not 1, then return NaN
    return unless $c->_is_one($a);

    ($v, $sign == 1 ? '+' : '-');
}

sub _modpow {
    # modulus of power ($x ** $y) % $z
    my ($c, $num, $exp, $mod) = @_;

    # a^b (mod 1) = 0 for all a and b
    if ($c->_is_one($mod)) {
        @$num = 0;
        return $num;
    }

    # 0^a (mod m) = 0 if m != 0, a != 0
    # 0^0 (mod m) = 1 if m != 0
    if ($c->_is_zero($num)) {
        if ($c->_is_zero($exp)) {
            @$num = 1;
        } else {
            @$num = 0;
        }
        return $num;
    }

    #  $num = $c->_mod($num, $mod);   # this does not make it faster

    my $acc = $c->_copy($num);
    my $t = $c->_one();

    my $expbin = $c->_as_bin($exp);
    $expbin =~ s/^0b//;
    my $len = length($expbin);
    while (--$len >= 0) {
        if (substr($expbin, $len, 1) eq '1') { # is_odd
            $t = $c->_mul($t, $acc);
            $t = $c->_mod($t, $mod);
        }
        $acc = $c->_mul($acc, $acc);
        $acc = $c->_mod($acc, $mod);
    }
    @$num = @$t;
    $num;
}

sub _gcd {
    # Greatest common divisor.

    my ($c, $x, $y) = @_;

    # gcd(0, 0) = 0
    # gcd(0, a) = a, if a != 0

    if (@$x == 1 && $x->[0] == 0) {
        if (@$y == 1 && $y->[0] == 0) {
            @$x = 0;
        } else {
            @$x = @$y;
        }
        return $x;
    }

    # Until $y is zero ...

    until (@$y == 1 && $y->[0] == 0) {

        # Compute remainder.

        $c->_mod($x, $y);

        # Swap $x and $y.

        my $tmp = $c->_copy($x);
        @$x = @$y;
        $y = $tmp;              # no deref here; that would modify input $y
    }

    return $x;
}

1;

=pod

=head1 NAME

Math::BigInt::Calc - pure Perl module to support Math::BigInt

=head1 SYNOPSIS

    # to use it with Math::BigInt
    use Math::BigInt lib => 'Calc';

    # to use it with Math::BigFloat
    use Math::BigFloat lib => 'Calc';

    # to use it with Math::BigRat
    use Math::BigRat lib => 'Calc';

    # explicitly set base length and whether to "use integer"
    use Math::BigInt::Calc base_len => 4, use_int => 1;
    use Math::BigInt lib => 'Calc';

=head1 DESCRIPTION

Math::BigInt::Calc inherits from Math::BigInt::Lib.

In this library, the numbers are represented interenally in base B = 10**N,
where N is the largest possible integer that does not cause overflow in the
intermediate computations. The base B elements are stored in an array, with the
least significant element stored in array element zero. There are no leading
zero elements, except a single zero element when the number is zero. For
instance, if B = 10000, the number 1234567890 is represented internally as
[7890, 3456, 12].

=head1 OPTIONS

When the module is loaded, it computes the maximum exponent, i.e., power of 10,
that can be used with and without "use integer" in the computations. The default
is to use this maximum exponent. If the combination of the 'base_len' value and
the 'use_int' value exceeds the maximum value, an error is thrown.

=over 4

=item base_len

The base length can be specified explicitly with the 'base_len' option. The
value must be a positive integer.

    use Math::BigInt::Calc base_len => 4;  # use 10000 as internal base

=item use_int

This option is used to specify whether "use integer" should be used in the
internal computations. The value is interpreted as a boolean value, so use 0 or
"" for false and anything else for true. If the 'base_len' is not specified
together with 'use_int', the current value for the base length is used.

    use Math::BigInt::Calc use_int => 1;   # use "use integer" internally

=back

=head1 METHODS

This overview constains only the methods that are specific to
C<Math::BigInt::Calc>. For the other methods, see L<Math::BigInt::Lib>.

=over 4

=item _base_len()

Specify the desired base length and whether to enable "use integer" in the
computations.

    Math::BigInt::Calc -> _base_len($base_len, $use_int);

Note that it is better to specify the base length and whether to use integers as
options when the module is loaded, for example like this

    use Math::BigInt::Calc base_len => 6, use_int => 1;

=back

=head1 SEE ALSO

L<Math::BigInt::Lib> for a description of the API.

Alternative libraries L<Math::BigInt::FastCalc>, L<Math::BigInt::GMP>,
L<Math::BigInt::Pari>, L<Math::BigInt::GMPz>, and L<Math::BigInt::BitVect>.

Some of the modules that use these libraries L<Math::BigInt>,
L<Math::BigFloat>, and L<Math::BigRat>.

=cut
¿Qué es la limpieza dental de perros? - Clínica veterinaria


Es la eliminación del sarro y la placa adherida a la superficie de los dientes mediante un equipo de ultrasonidos que garantiza la integridad de las piezas dentales a la vez que elimina en profundidad cualquier resto de suciedad.

A continuación se procede al pulido de los dientes mediante una fresa especial que elimina la placa bacteriana y devuelve a los dientes el aspecto sano que deben tener.

Una vez terminado todo el proceso, se mantiene al perro en observación hasta que se despierta de la anestesia, bajo la atenta supervisión de un veterinario.

¿Cada cuánto tiempo tengo que hacerle una limpieza dental a mi perro?

A partir de cierta edad, los perros pueden necesitar una limpieza dental anual o bianual. Depende de cada caso. En líneas generales, puede decirse que los perros de razas pequeñas suelen acumular más sarro y suelen necesitar una atención mayor en cuanto a higiene dental.


Riesgos de una mala higiene


Los riesgos más evidentes de una mala higiene dental en los perros son los siguientes:

  • Cuando la acumulación de sarro no se trata, se puede producir una inflamación y retracción de las encías que puede descalzar el diente y provocar caídas.
  • Mal aliento (halitosis).
  • Sarro perros
  • Puede ir a más
  • Las bacterias de la placa pueden trasladarse a través del torrente circulatorio a órganos vitales como el corazón ocasionando problemas de endocarditis en las válvulas. Las bacterias pueden incluso acantonarse en huesos (La osteomielitis es la infección ósea, tanto cortical como medular) provocando mucho dolor y una artritis séptica).

¿Cómo se forma el sarro?

El sarro es la calcificación de la placa dental. Los restos de alimentos, junto con las bacterias presentes en la boca, van a formar la placa bacteriana o placa dental. Si la placa no se retira, al mezclarse con la saliva y los minerales presentes en ella, reaccionará formando una costra. La placa se calcifica y se forma el sarro.

El sarro, cuando se forma, es de color blanquecino pero a medida que pasa el tiempo se va poniendo amarillo y luego marrón.

Síntomas de una pobre higiene dental
La señal más obvia de una mala salud dental canina es el mal aliento.

Sin embargo, a veces no es tan fácil de detectar
Y hay perros que no se dejan abrir la boca por su dueño. Por ejemplo…

Recientemente nos trajeron a la clínica a un perro que parpadeaba de un ojo y decía su dueño que le picaba un lado de la cara. Tenía molestias y dificultad para comer, lo que había llevado a sus dueños a comprarle comida blanda (que suele ser un poco más cara y llevar más contenido en grasa) durante medio año. Después de una exploración oftalmológica, nos dimos cuenta de que el ojo tenía una úlcera en la córnea probablemente de rascarse . Además, el canto lateral del ojo estaba inflamado. Tenía lo que en humanos llamamos flemón pero como era un perro de pelo largo, no se le notaba a simple vista. Al abrirle la boca nos llamó la atención el ver una muela llena de sarro. Le realizamos una radiografía y encontramos una fístula que llegaba hasta la parte inferior del ojo.

Le tuvimos que extraer la muela. Tras esto, el ojo se curó completamente con unos colirios y una lentilla protectora de úlcera. Afortunadamente, la úlcera no profundizó y no perforó el ojo. Ahora el perro come perfectamente a pesar de haber perdido una muela.

¿Cómo mantener la higiene dental de tu perro?
Hay varias maneras de prevenir problemas derivados de la salud dental de tu perro.

Limpiezas de dientes en casa
Es recomendable limpiar los dientes de tu perro semanal o diariamente si se puede. Existe una gran variedad de productos que se pueden utilizar:

Pastas de dientes.
Cepillos de dientes o dedales para el dedo índice, que hacen más fácil la limpieza.
Colutorios para echar en agua de bebida o directamente sobre el diente en líquido o en spray.

En la Clínica Tus Veterinarios enseñamos a nuestros clientes a tomar el hábito de limpiar los dientes de sus perros desde que son cachorros. Esto responde a nuestro compromiso con la prevención de enfermedades caninas.

Hoy en día tenemos muchos clientes que limpian los dientes todos los días a su mascota, y como resultado, se ahorran el dinero de hacer limpiezas dentales profesionales y consiguen una mejor salud de su perro.


Limpiezas dentales profesionales de perros y gatos

Recomendamos hacer una limpieza dental especializada anualmente. La realizamos con un aparato de ultrasonidos que utiliza agua para quitar el sarro. Después, procedemos a pulir los dientes con un cepillo de alta velocidad y una pasta especial. Hacemos esto para proteger el esmalte.

La frecuencia de limpiezas dentales necesaria varía mucho entre razas. En general, las razas grandes tienen buena calidad de esmalte, por lo que no necesitan hacerlo tan a menudo e incluso pueden pasarse la vida sin requerir una limpieza. Sin embargo, razas pequeñas como el Yorkshire o el Maltés, deben hacérselas todos los años desde cachorros si se quiere conservar sus piezas dentales.

Otro factor fundamental es la calidad del pienso. Algunas marcas han diseñado croquetas que limpian la superficie del diente y de la muela al masticarse.

Ultrasonido para perros

¿Se necesita anestesia para las limpiezas dentales de perros y gatos?

La limpieza dental en perros no es una técnica que pueda practicarse sin anestesia general , aunque hay veces que los propietarios no quieren anestesiar y si tiene poco sarro y el perro es muy bueno se puede intentar…… , pero no se va a poder pulir ni acceder a todas la zona de la boca …. Además los limpiadores dentales van a irrigar agua y hay riesgo de aspiración a vías respiratorias si no se realiza una anestesia correcta con intubación traqueal . En resumen , sin anestesia no se va hacer una correcta limpieza dental.

Tampoco sirve la sedación ya que necesitamos que el animal esté totalmente quieto, y el veterinario tenga un acceso completo a todas sus piezas dentales y encías.

Alimentos para la limpieza dental

Hay que tener cierto cuidado a la hora de comprar determinados alimentos porque no todos son saludables. Algunos tienen demasiado contenido graso, que en exceso puede causar problemas cardiovasculares y obesidad.

Los mejores alimentos para los dientes son aquellos que están elaborados por empresas farmacéuticas y llevan componentes químicos con tratamientos específicos para el diente del perro. Esto implica no solo limpieza a través de la acción mecánica de morder sino también un tratamiento antibacteriano para prevenir el sarro.

Conclusión

Si eres como la mayoría de dueños, por falta de tiempo , es probable que no estés prestando la suficiente atención a la limpieza dental de tu perro. Por eso te animamos a que comiences a limpiar los dientes de tu perro y consideres atender a su higiene bucal con frecuencia.

Estas simples medidas pueden conllevar a que tu perro tenga una vida más larga y mucho más saludable.

Si te resulta imposible introducir un cepillo de dientes a tu perro en la boca, pásate con él por clínica Tus Veterinarios y te explicamos cómo hacerlo.

Necesitas hacer una limpieza dental profesional a tu mascota?
Llámanos al 622575274 o contacta con nosotros

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

¡Hola!