2463 lines
70 KiB
Perl
2463 lines
70 KiB
Perl
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
|