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

📄 calc.pm

📁 网页留言本,比一般的留言簿管用
💻 PM
📖 第 1 页 / 共 4 页
字号:
  # compute number of digits  # 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, negative values count backward  # zero is rightmost, so _digit(123,0) will give 3  my ($c,$x,$n) = @_;  my $len = _len('',$x);  $n = $len+$n if $n < 0;		# -1 last, -2 second-to-last  $n = abs($n);				# if negative was too big  $len--; $n = $len if $n > $len;	# n to big?    my $elem = int($n / $BASE_LEN);	# which array element  my $digit = $n % $BASE_LEN;		# which digit in this element  $elem = '0000'.@$x[$elem];		# get element padded with 0's  substr($elem,-$digit-1,1);  }sub _zeros  {  # return amount of trailing zeros in decimal  # check each array elem in _m for having 0 at end as long as elem == 0  # Upon finding a elem != 0, stop  my $x = $_[1];  my $zeros = 0; my $elem;  foreach my $e (@$x)    {    if ($e != 0)      {      $elem = "$e";				# preserve x      $elem =~ s/.*?(0*$)/$1/;			# strip anything not zero      $zeros *= $BASE_LEN;			# elems * 5      $zeros += length($elem);			# count trailing zeros      last;					# early out      }    $zeros ++;					# real else branch: 50% slower!    }  $zeros;  }############################################################################### _is_* routinessub _is_zero  {  # return true if arg (BINT or num_str) is zero (array '+', '0')  my $x = $_[1];  (((scalar @$x == 1) && ($x->[0] == 0))) <=> 0;  }sub _is_even  {  # return true if arg (BINT or num_str) is even  my $x = $_[1];  (!($x->[0] & 1)) <=> 0;   }sub _is_odd  {  # return true if arg (BINT or num_str) is even  my $x = $_[1];  (($x->[0] & 1)) <=> 0;   }sub _is_one  {  # return true if arg (BINT or num_str) is one (array '+', '1')  my $x = $_[1];  (scalar @$x == 1) && ($x->[0] == 1) <=> 0;   }sub __strip_zeros  {  # internal normalization function that strips leading zeros from the array  # args: ref to array  my $s = shift;   my $cnt = scalar @$s; # get count of parts  my $i = $cnt-1;  push @$s,0 if $i < 0;		# div might return empty results, so fix it  return $s if @$s == 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)  while ($i > 0) { last if $s->[$i] != 0; $i--; }  $i++; splice @$s,$i if ($i < $cnt); # $i cant be 0  $s;                                                                      }                                                                             ################################################################################ check routine to test internal state of corruptionssub _check  {  # used by the test suite  my $x = $_[1];  return "$x is not a reference" if !ref($x);  # are all parts are valid?  my $i = 0; my $j = scalar @$x; my ($e,$try);  while ($i < $j)    {    $e = $x->[$i]; $e = 'undef' unless defined $e;    $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e)";    last if $e !~ /^[+]?[0-9]+$/;    $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (stringify)";    last if "$e" !~ /^[+]?[0-9]+$/;    $try = '=~ /^[\+]?[0-9]+\$/; '."($x, $e) (cat-stringify)";    last if '' . "$e" !~ /^[+]?[0-9]+$/;    $try = ' < 0 || >= $BASE; '."($x, $e)";    last if $e <0 || $e >= $BASE;    # this test is disabled, since new/bnorm and certain ops (like early out    # in add/sub) are allowed/expected to leave '00000' in some elements    #$try = '=~ /^00+/; '."($x, $e)";    #last if $e =~ /^00+/;    $i++;    }  return "Illegal part '$e' at pos $i (tested: $try)" if $i < $j;  return 0;  }############################################################################################################################################################### some optional routines to make BigInt fastersub _mod  {  # if possible, use mod shortcut  my ($c,$x,$yo) = @_;  # slow way since $y to big  if (scalar @$yo > 1)    {    my ($xo,$rem) = _div($c,$x,$yo);    return $rem;    }  my $y = $yo->[0];  # both are single element arrays  if (scalar @$x == 1)    {    $x->[0] %= $y;    return $x;    }  # @y is single element, but @x has more than one  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 trough all elements: 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 trough all elements: 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;    }  splice (@$x,1);  $x;  }############################################################################### shiftssub _rsft  {  my ($c,$x,$y,$n) = @_;  if ($n != 10)    {    $n = _new($c,\$n); return _div($c,$x, _pow($c,$n,$y));    }  # shortcut (faster) for shifting by 10)  # multiples of $BASE_LEN  my $dst = 0;				# destination  my $src = _num($c,$y);		# as normal int  my $xlen = (@$x-1)*$BASE_LEN+length(int($x->[-1]));  # len of x in digits  if ($src > $xlen)    {    # 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 = scalar @$x - $src;	# elems to go    my $vd; my $z = '0'x $BASE_LEN;    $x->[scalar @$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,$y,$n) = @_;  if ($n != 10)    {    $n = _new($c,\$n); return _mul($c,$x, _pow($c,$n,$y));    }  # shortcut (faster) for shifting by 10) since we are in base 10eX  # multiples of $BASE_LEN:  my $src = scalar @$x;			# source  my $len = _num($c,$y);		# shift-len as normal int  my $rem = $len % $BASE_LEN;		# remainder to shift  my $dst = $src + int($len/$BASE_LEN);	# destination  my $vd;				# further speedup  $x->[$src] = 0;			# avoid first ||0 for speed  my $z = '0' x $BASE_LEN;  while ($src >= 0)    {    $vd = $x->[$src]; $vd = $z.$vd;    $vd = substr($vd,-$BASE_LEN+$rem,$BASE_LEN-$rem);    $vd .= $src > 0 ? substr($z.$x->[$src-1],-$BASE_LEN,$rem) : '0' x $rem;    $vd = substr($vd,-$BASE_LEN,$BASE_LEN) if length($vd) > $BASE_LEN;    $x->[$dst] = int($vd);    $dst--; $src--;    }  # set lowest parts to 0  while ($dst >= 0) { $x->[$dst--] = 0; }  # fix spurios last zero element  splice @$x,-1 if $x->[-1] == 0;  $x;  }sub _pow  {  # power of $x to $y  # ref to array, ref to array, return ref to array  my ($c,$cx,$cy) = @_;  my $pow2 = _one();  my $y_bin = ${_as_bin($c,$cy)}; $y_bin =~ s/^0b//;  my $len = length($y_bin);  while (--$len > 0)    {    _mul($c,$pow2,$cx) if substr($y_bin,$len,1) eq '1';		# is odd?    _mul($c,$cx,$cx);    }  _mul($c,$cx,$pow2);  $cx;  }sub _fac  {  # factorial of $x  # ref to array, return ref to array  my ($c,$cx) = @_;  if ((@$cx == 1) && ($cx->[0] <= 2))    {    $cx->[0] = 1 * ($cx->[0]||1); # 0,1 => 1, 2 => 2    return $cx;    }  # go forward until $base is exceeded  # limit is either $x or $base (x == 100 means as result too high)  my $steps = 100; $steps = $cx->[0] if @$cx == 1;  my $r = 2; my $cf = 3; my $step = 1; my $last = $r;  while ($r < $BASE && $step < $steps)    {    $last = $r; $r *= $cf++; $step++;    }  if ((@$cx == 1) && ($step == $cx->[0]))    {    # completely done    $cx = [$last];    return $cx;    }  my $n = _copy($c,$cx);  $cx = [$last];  while (!(@$n == 1 && $n->[0] == $step))    {    _mul($c,$cx,$n); _dec($c,$n);    }  $cx;  }# for debugging:  use constant DEBUG => 0;  my $steps = 0;  sub steps { $steps };sub _sqrt  {  # square-root of $x in place  # Compute a guess of the result (rule of thumb), then improve it via  # Newton's method.  my ($c,$x) = @_;  if (scalar @$x == 1)    {    # fit's into one Perl scalar, so result can be computed directly    $x->[0] = int(sqrt($x->[0]));    return $x;    }   my $y = _copy($c,$x);  # hopefully _len/2 is < $BASE, the -1 is to always undershot the guess  # since our guess will "grow"  my $l = int((_len($c,$x)-1) / 2);	  my $lastelem = $x->[-1];					# for guess  my $elems = scalar @$x - 1;  # not enough digits, but could have more?  if ((length($lastelem) <= 3) && ($elems > 1))    {    # right-align with zero pad    my $len = length($lastelem) & 1;    print "$lastelem => " if DEBUG;    $lastelem .= substr($x->[-2] . '0' x $BASE_LEN,0,$BASE_LEN);    # former odd => make odd again, or former even to even again    $lastelem = $lastelem / 10 if (length($lastelem) & 1) != $len;    print "$lastelem\n" if DEBUG;    }  # construct $x (instead of _lsft($c,$x,$l,10)  my $r = $l % $BASE_LEN;	# 10000 00000 00000 00000 ($BASE_LEN=5)  $l = int($l / $BASE_LEN);  print "l =  $l " if DEBUG;  splice @$x,$l;		# keep ref($x), but modify it  # we make the first part of the guess not '1000...0' but int(sqrt($lastelem))  # that gives us:  # 14400 00000 => sqrt(14400) => guess first digits to be 120  # 144000 000000 => sqrt(144000) => guess 379  print "$lastelem (elems $elems) => " if DEBUG;  $lastelem = $lastelem / 10 if ($elems & 1 == 1);		# odd or even?  my $g = sqrt($lastelem); $g =~ s/\.//;			# 2.345 => 2345  $r -= 1 if $elems & 1 == 0;					# 70 => 7  # padd with zeros if result is too short  $x->[$l--] = int(substr($g . '0' x $r,0,$r+1));  print "now ",$x->[-1] if DEBUG;  print " would have been ", int('1' . '0' x $r),"\n" if DEBUG;  # If @$x > 1, we could compute the second elem of the guess, too, to create  # an even better guess. Not implemented yet. Does it improve performance?  $x->[$l--] = 0 while ($l >= 0);	# all other digits of guess are zero  print "start x= ",${_str($c,$x)},"\n" if DEBUG;  my $two = _two();  my $last = _zero();  my $lastlast = _zero();  $steps = 0 if DEBUG;  while (_acmp($c,$last,$x) != 0 && _acmp($c,$lastlast,$x) != 0)    {    $steps++ if DEBUG;    $lastlast = _copy($c,$last);    $last = _copy($c,$x);    _add($c,$x, _div($c,_copy($c,$y),$x));    _div($c,$x, $two );    print " x= ",${_str($c,$x)},"\n" if DEBUG;    }  print "\nsteps in sqrt: $steps, " if DEBUG;  _dec($c,$x) if _acmp($c,$y,_mul($c,_copy($c,$x),$x)) < 0;	# overshot?   print " final ",$x->[-1],"\n" if DEBUG;  $x;  }############################################################################### binary stuffsub _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 _acmp($c,$x,$y) == 0;		# shortcut    my $m = _one(); my ($xr,$yr);

⌨️ 快捷键说明

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