⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 calc.pm

📁 source of perl for linux application,
💻 PM
📖 第 1 页 / 共 5 页
字号:
package Math::BigInt::Calc;use 5.006;use strict;# use warnings;	# dont use warnings for older Perlsour $VERSION = '0.52';# 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...)# USE_MUL: due to problems on certain os (os390, posix-bc) "* 1e-5" is used# instead of "/ 1e5" at some places, (marked with USE_MUL). Other platforms# BS2000, some Crays need USE_DIV instead.# The BEGIN block is used to determine which of the two variants gives the# correct result.# Beware of things like:# $i = $i * $y + $car; $car = int($i / $BASE); $i = $i % $BASE;# This works on x86, but fails on ARM (SA1100, iPAQ) due to whoknows what# reasons. So, use this instead (slower, but correct):# $i = $i * $y + $car; $car = int($i / $BASE); $i -= $BASE * $car;############################################################################### global constants, flags and accessory# announce that we are compatible with MBI v1.83 and upsub api_version () { 2; } # constants for easier lifemy ($BASE,$BASE_LEN,$RBASE,$MAX_VAL);my ($AND_BITS,$XOR_BITS,$OR_BITS);my ($AND_MASK,$XOR_MASK,$OR_MASK);sub _base_len   {  # Set/get the BASE_LEN and assorted other, connected values.  # Used only by the testsuite, the set variant is used only by the BEGIN  # block below:  shift;  my ($b, $int) = @_;  if (defined $b)    {    # avoid redefinitions    undef &_mul;    undef &_div;    if ($] >= 5.008 && $int && $b > 7)      {      $BASE_LEN = $b;      *_mul = \&_mul_use_div_64;      *_div = \&_div_use_div_64;      $BASE = int("1e".$BASE_LEN);      $MAX_VAL = $BASE-1;      return $BASE_LEN unless wantarray;      return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL, $BASE);      }    # find whether we can use mul or div in mul()/div()    $BASE_LEN = $b+1;    my $caught = 0;    while (--$BASE_LEN > 5)      {      $BASE = int("1e".$BASE_LEN);      $RBASE = abs('1e-'.$BASE_LEN);			# see USE_MUL      $caught = 0;      $caught += 1 if (int($BASE * $RBASE) != 1);	# should be 1      $caught += 2 if (int($BASE / $BASE) != 1);	# should be 1      last if $caught != 3;      }    $BASE = int("1e".$BASE_LEN);    $RBASE = abs('1e-'.$BASE_LEN);			# see USE_MUL    $MAX_VAL = $BASE-1;       # ($caught & 1) != 0 => cannot use MUL    # ($caught & 2) != 0 => cannot use DIV    if ($caught == 2)				# 2      {      # must USE_MUL since we cannot use DIV      *_mul = \&_mul_use_mul;      *_div = \&_div_use_mul;      }    else					# 0 or 1      {      # can USE_DIV instead      *_mul = \&_mul_use_div;      *_div = \&_div_use_div;      }    }  return $BASE_LEN unless wantarray;  return ($BASE_LEN, $AND_BITS, $XOR_BITS, $OR_BITS, $BASE_LEN, $MAX_VAL, $BASE);  }sub _new  {  # (ref to string) return ref to num_array  # Convert a number from string format (without sign) to internal base  # 1ex format. Assumes normalized value as input.  my $il = length($_[1])-1;  # < BASE_LEN due len-1 above  return [ int($_[1]) ] if $il < $BASE_LEN;	# shortcut for short numbers  # this leaves '00000' instead of int 0 and will be corrected after any op  [ reverse(unpack("a" . ($il % $BASE_LEN+1)     . ("a$BASE_LEN" x ($il / $BASE_LEN)), $_[1])) ];  }                                                                             BEGIN  {  # from Daniel Pfeiffer: determine largest group of digits that is precisely  # multipliable with itself plus carry  # Test now changed to expect the proper pattern, not a result off by 1 or 2  my ($e, $num) = 3;	# lowest value we will use is 3+1-1 = 3  do     {    $num = ('9' x ++$e) + 0;    $num *= $num + 1.0;    } while ("$num" =~ /9{$e}0{$e}/);	# must be a certain pattern  $e--; 				# last test failed, so retract one step  # the limits below brush the problems with the test above under the rug:  # the test should be able to find the proper $e automatically  $e = 5 if $^O =~ /^uts/;	# UTS get's some special treatment  $e = 5 if $^O =~ /^unicos/;	# unicos is also problematic (6 seems to work				# there, but we play safe)  my $int = 0;  if ($e > 7)    {    use integer;    my $e1 = 7;    $num = 7;    do       {      $num = ('9' x ++$e1) + 0;      $num *= $num + 1;      } while ("$num" =~ /9{$e1}0{$e1}/);	# must be a certain pattern    $e1--; 					# last test failed, so retract one step    if ($e1 > 7)      {       $int = 1; $e = $e1;       }    }   __PACKAGE__->_base_len($e,$int);	# set and store  use integer;  # find out how many bits _and, _or and _xor can take (old default = 16)  # I don't think anybody has yet 128 bit scalars, so let's play safe.  local $^W = 0;	# don't warn about 'nonportable number'  $AND_BITS = 15; $XOR_BITS = 15; $OR_BITS = 15;  # find max bits, we will not go higher than numberofbits that fit into $BASE  # to make _and etc simpler (and faster for smaller, slower for large numbers)  my $max = 16;  while (2 ** $max < $BASE) { $max++; }  {    no integer;    $max = 16 if $] < 5.006;	# older Perls might not take >16 too well  }  my ($x,$y,$z);  do {    $AND_BITS++;    $x = CORE::oct('0b' . '1' x $AND_BITS); $y = $x & $x;    $z = (2 ** $AND_BITS) - 1;    } while ($AND_BITS < $max && $x == $z && $y == $x);  $AND_BITS --;						# retreat one step  do {    $XOR_BITS++;    $x = CORE::oct('0b' . '1' x $XOR_BITS); $y = $x ^ 0;    $z = (2 ** $XOR_BITS) - 1;    } while ($XOR_BITS < $max && $x == $z && $y == $x);  $XOR_BITS --;						# retreat one step  do {    $OR_BITS++;    $x = CORE::oct('0b' . '1' x $OR_BITS); $y = $x | $x;    $z = (2 ** $OR_BITS) - 1;    } while ($OR_BITS < $max && $x == $z && $y == $x);  $OR_BITS --;						# retreat one step    $AND_MASK = __PACKAGE__->_new( ( 2 ** $AND_BITS ));  $XOR_MASK = __PACKAGE__->_new( ( 2 ** $XOR_BITS ));  $OR_MASK = __PACKAGE__->_new( ( 2 ** $OR_BITS ));  # We can compute the approximate lenght no faster than the real length:  *_alen = \&_len;  }###############################################################################sub _zero  {  # create a zero  [ 0 ];  }sub _one  {  # create a one  [ 1 ];  }sub _two  {  # create a two (used internally for shifting)  [ 2 ];  }sub _ten  {  # create a 10 (used internally for shifting)  [ 10 ];  }sub _1ex  {  # create a 1Ex  my $rem = $_[1] % $BASE_LEN;		# remainder  my $parts = $_[1] / $BASE_LEN;	# parts  # 000000, 000000, 100   [ (0) x $parts, '1' . ('0' x $rem) ];  }sub _copy  {  # make a true copy  [ @{$_[1]} ];  }# catch and throw awaysub import { }############################################################################### convert back to string and numbersub _str  {  # (ref to BINT) return num_str  # Convert number from internal base 100000 format to string format.  # internal format is always normalized (no leading zeros, "-0" => "+0")  my $ar = $_[1];  my $l = scalar @$ar;				# number of parts  if ($l < 1)					# should not happen    {    require Carp;    Carp::croak("$_[1] has no elements");    }  my $ret = "";  # handle first one different to strip leading zeros from it (there are no  # leading zero parts in internal representation)  $l --; $ret .= int($ar->[$l]); $l--;  # 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 ($l >= 0)    {    $ret .= substr($z.$ar->[$l],-$BASE_LEN); # fastest way I could think of    $l--;    }  $ret;  }                                                                             sub _num  {  # Make a number (scalar int/float) from a BigInt object   my $x = $_[1];  return 0+$x->[0] if scalar @$x == 1;  # below $BASE  my $fac = 1;  my $num = 0;  foreach (@$x)    {    $num += $fac*$_; $fac *= $BASE;    }  $num;   }############################################################################### actual math codesub _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 clobbers up array x, but not y.   my ($c,$x,$y) = @_;  return $x if (@$y == 1) && $y->[0] == 0;		# $x + 0 => $x  if ((@$x == 1) && $x->[0] == 0)			# 0 + $y => $y->copy    {    # twice as slow as $x = [ @$y ], but nec. to retain $x as ref :(    @$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 $i; my $car = 0; my $j = 0;  for $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) == 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 $i; my $j = 0;  if (!$s)    {    for $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 $i (@$sx)    {    # we can't do an early out if $x is < than $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_mul  {  # (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)         {         $xv->[0] = $xv->[0] - ($xv->[1] = int($xv->[0] * $RBASE)) * $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 = int($i * $RBASE); $i -= $car * $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 = [@$xv] if $xv == $yv;	# same references?  my @prod = (); my ($prod,$car,$cty,$xi,$yi);  for $xi (@$xv)    {    $car = 0; $cty = 0;    # slow variant#    for $yi (@$yv)#      {#      $prod = $xi * $yi + ($prod[$cty] || 0) + $car;#      $prod[$cty++] =#       $prod - ($car = int($prod * RBASE)) * $BASE;  # see USE_MUL#      }#    $prod[$cty] += $car if $car; # need really to check for 0?#    $xi = shift @prod;    # faster variant    # looping through this if $xi == 0 is silly - so optimize it away!    $xi = (shift @prod || 0), next if $xi == 0;    for $yi (@$yv)      {      $prod = $xi * $yi + ($prod[$cty] || 0) + $car;##     this is actually a tad slower##        $prod = $prod[$cty]; $prod += ($car + $xi * $yi);	# no ||0 here      $prod[$cty++] =       $prod - ($car = int($prod * $RBASE)) * $BASE;  # see USE_MUL      }    $prod[$cty] += $car if $car; # need really to check for 0?    $xi = shift @prod || 0;	# || 0 makes v5.005_3 happy    }  push @$xv, @prod;  # can't have leading zeros#  __strip_zeros($xv);  $xv;  }                                                                             sub _mul_use_div_64  {  # (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 small numbers, also handles $x == 0    if (@$xv == 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->[0] *= $yv->[0]) >= $BASE)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -