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

📄 calc.pm

📁 source of perl for linux application,
💻 PM
📖 第 1 页 / 共 5 页
字号:
      }    else       {      @d = @$x;      }    @$x = @q;    my $d = \@d;     __strip_zeros($x);    __strip_zeros($d);    return ($x,$d);    }  @$x = @q;  __strip_zeros($x);  $x;  }sub _div_use_div  {  # ref to array, ref to array, modify first array and return remainder if   # in list context  my ($c,$x,$yorg) = @_;  # the general div algorithmn here is about O(N*N) and thus quite slow, so  # we first check for some special cases and use shortcuts to handle them.  # This works, because we store the numbers in a chunked format where each  # element contains 5..7 digits (depending on system).  # if both numbers have only one element:  if (@$x == 1 && @$yorg == 1)    {    # shortcut, $yorg and $x are two small numbers    if (wantarray)      {      my $r = [ $x->[0] % $yorg->[0] ];      $x->[0] = int($x->[0] / $yorg->[0]);      return ($x,$r);       }    else      {      $x->[0] = int($x->[0] / $yorg->[0]);      return $x;       }    }  # if x has more than one, but y has only one element:  if (@$yorg == 1)    {    my $rem;    $rem = _mod($c,[ @$x ],$yorg) if wantarray;    # shortcut, $y is < $BASE    my $j = scalar @$x; my $r = 0;     my $y = $yorg->[0]; my $b;    while ($j-- > 0)      {      $b = $r * $BASE + $x->[$j];      $x->[$j] = int($b/$y);      $r = $b % $y;      }    pop @$x if @$x > 1 && $x->[-1] == 0;	# splice up a leading 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 yet, the result will be 0  if (@$yorg > @$x)    {    my $rem;    $rem = [@$x] if wantarray;			# make copy    splice (@$x,1);				# keep ref to original array    $x->[0] = 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 $rem;    # if $yorg has more digits than $x (it's leading element is longer than    # the one from $x), the result will also be 0:    if (length(int($yorg->[-1])) > length(int($x->[-1])))      {      $rem = [@$x] if wantarray;		# make copy      splice (@$x,1);				# keep ref to org array      $x->[0] = 0;				# set to 0      return ($x,$rem) if wantarray;		# including remainder?      return $x;      }    # now calculate $x / $yorg    if (length(int($yorg->[-1])) == length(int($x->[-1])))      {      # same length, so make full compare      my $a = 0; my $j = scalar @$x - 1;      # manual way (abort if unequal, good for early ne)      while ($j >= 0)        {        last if ($a = $x->[$j] - $yorg->[$j]); $j--;        }      # $a contains the result of the compare between X and Y      # a < 0: x < y, a == 0: x == y, a > 0: x > y      if ($a <= 0)        {        $rem = [ 0 ];			# a = 0 => x == y => rem 0        $rem = [@$x] if $a != 0;	# a < 0 => x < y => rem = x        splice(@$x,1);			# keep single element        $x->[0] = 0;			# if $a < 0        $x->[0] = 1 if $a == 0; 	# $x == $y        return ($x,$rem) if wantarray;	# including remainder?        return $x;        }      # $x >= $y, so proceed normally      }    }  # all other cases:  my $y = [ @$yorg ];				# always make copy to preserve   my ($car,$bar,$prd,$dd,$xi,$yi,@q,$v2,$v1,@d,$tmp,$q,$u2,$u1,$u0);  $car = $bar = $prd = 0;  if (($dd = int($BASE/($y->[-1]+1))) != 1)     {    for $xi (@$x)       {      $xi = $xi * $dd + $car;      $xi -= ($car = int($xi / $BASE)) * $BASE;      }    push(@$x, $car); $car = 0;    for $yi (@$y)       {      $yi = $yi * $dd + $car;      $yi -= ($car = int($yi / $BASE)) * $BASE;      }    }  else     {    push(@$x, 0);    }  # @q will accumulate the final result, $q contains the current computed  # part of the final result  @q = (); ($v2,$v1) = @$y[-2,-1];  $v2 = 0 unless $v2;  while ($#$x > $#$y)     {    ($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;    $q = (($u0 == $v1) ? $MAX_VAL : int(($u0*$BASE+$u1)/$v1));    --$q while ($v2*$q > ($u0*$BASE+$u1-$q*$v1)*$BASE+$u2);    if ($q)      {      ($car, $bar) = (0,0);      for ($yi = 0, $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 ($yi = 0, $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)     {    @d = ();    if ($dd != 1)        {      $car = 0;       for $xi (reverse @$x)         {        $prd = $car * $BASE + $xi;        $car = $prd - ($tmp = int($prd / $dd)) * $dd;        unshift(@d, $tmp);        }      }    else       {      @d = @$x;      }    @$x = @q;    my $d = \@d;     __strip_zeros($x);    __strip_zeros($d);    return ($x,$d);    }  @$x = @q;  __strip_zeros($x);  $x;  }############################################################################### testingsub _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 scalar @$cx == scalar @$cy && scalar @$cx == 1;  # fast comp based on number of array elements (aka pseudo-length)  my $lxy = (scalar @$cx - scalar @$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 = scalar @$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, 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 = '0' x $BASE_LEN . @$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];  return 0 if scalar @$x == 1 && $x->[0] == 0;  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 is zero   (((scalar @{$_[1]} == 1) && ($_[1]->[0] == 0))) <=> 0;  }sub _is_even  {  # return true if arg is even  (!($_[1]->[0] & 1)) <=> 0;   }sub _is_odd  {  # return true if arg is even  (($_[1]->[0] & 1)) <=> 0;   }sub _is_one  {  # return true if arg is one  (scalar @{$_[1]} == 1) && ($_[1]->[0] == 1) <=> 0;   }sub _is_two  {  # return true if arg is two   (scalar @{$_[1]} == 1) && ($_[1]->[0] == 2) <=> 0;   }sub _is_ten  {  # return true if arg is ten   (scalar @{$_[1]} == 1) && ($_[1]->[0] == 10) <=> 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 for 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;  0;  }###############################################################################sub _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 a single element, but @x has more than one 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: 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: 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);		# keep one element of $x  $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 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;

⌨️ 快捷键说明

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