Term::Complete problem + fix (Was: Re: muttprofile + perl 5.8)
[p5sagit/p5-mst-13.2.git] / lib / Math / BigFloat.pm
index c9624ba..4e93a2f 100644 (file)
@@ -12,12 +12,10 @@ package Math::BigFloat;
 #   _p: precision
 #   _f: flags, used to signal MBI not to touch our private parts
 
-$VERSION = '1.33';
+$VERSION = '1.38';
 require 5.005;
 use Exporter;
-use File::Spec;
-# use Math::BigInt;
-@ISA =       qw( Exporter Math::BigInt);
+@ISA =       qw(Exporter Math::BigInt);
 
 use strict;
 use vars qw/$AUTOLOAD $accuracy $precision $div_scale $round_mode $rnd_mode/;
@@ -307,9 +305,10 @@ sub bsstr
     return $x->{sign} unless $x->{sign} eq '+inf';      # -inf, NaN
     return 'inf';                                       # +inf
     }
-  my $sign = $x->{_e}->{sign}; $sign = '' if $sign eq '-';
-  my $sep = 'e'.$sign;
-  $x->{_m}->bstr().$sep.$x->{_e}->bstr();
+  my $esign = $x->{_e}->{sign}; $esign = '' if $esign eq '-';
+  my $sep = 'e'.$esign;
+  my $sign = $x->{sign}; $sign = '' if $sign eq '+';
+  $sign . $x->{_m}->bstr() . $sep . $x->{_e}->bstr();
   }
     
 sub numify 
@@ -335,7 +334,17 @@ sub bcmp
   {
   # Compares 2 values.  Returns one of undef, <0, =0, >0. (suitable for sort)
   # (BFLOAT or num_str, BFLOAT or num_str) return cond_code
-  my ($self,$x,$y) = objectify(2,@_);
+
+  # set up parameters
+  my ($self,$x,$y) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y) = objectify(2,@_);
+    }
+
+  return $upgrade->bcmp($x,$y) if defined $upgrade &&
+    ((!$x->isa($self)) || (!$y->isa($self)));
 
   if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
     {
@@ -391,7 +400,17 @@ sub bacmp
   # Compares 2 values, ignoring their signs. 
   # Returns one of undef, <0, =0, >0. (suitable for sort)
   # (BFLOAT or num_str, BFLOAT or num_str) return cond_code
-  my ($self,$x,$y) = objectify(2,@_);
+  
+  # set up parameters
+  my ($self,$x,$y) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y) = objectify(2,@_);
+    }
+
+  return $upgrade->bacmp($x,$y) if defined $upgrade &&
+    ((!$x->isa($self)) || (!$y->isa($self)));
 
   # handle +-inf and NaN's
   if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/)
@@ -438,7 +457,14 @@ sub badd
   {
   # add second arg (BFLOAT or string) to first (BFLOAT) (modifies first)
   # return result as BFLOAT
-  my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+
+  # set up parameters
+  my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+    }
 
   # inf and NaN handling
   if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
@@ -503,7 +529,14 @@ sub bsub
   {
   # (BigFloat or num_str, BigFloat or num_str) return BigFloat
   # subtract second arg from first, modify first
-  my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+
+  # set up parameters
+  my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+    }
 
   if ($y->is_zero())           # still round for not adding zero
     {
@@ -611,6 +644,7 @@ sub blog
     {
     # simulate old behaviour
     $params[1] = $self->div_scale();   # and round to it as accuracy
+    $params[0] = undef;
     $scale = $params[1]+4;             # at least four more for proper round
     $params[3] = $r;                   # round mode by caller or undef
     $fallback = 1;                     # to clear a/p afterwards
@@ -624,10 +658,10 @@ sub blog
 
   return $x->bzero(@params) if $x->is_one();
   return $x->bnan() if $x->{sign} ne '+' || $x->is_zero();
-  #return $x->bone('+',@params) if $x->bcmp($base) == 0;
+  return $x->bone('+',@params) if $x->bcmp($base) == 0;
 
   # when user set globals, they would interfere with our calculation, so
-  # disable then and later re-enable them
+  # disable them and later re-enable them
   no strict 'refs';
   my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
   my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
@@ -787,7 +821,14 @@ sub bmul
   { 
   # multiply two numbers -- stolen from Knuth Vol 2 pg 233
   # (BINT or num_str, BINT or num_str) return BINT
-  my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+  
+  # set up parameters
+  my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+    }
 
   return $x->bnan() if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
 
@@ -820,7 +861,14 @@ sub bdiv
   {
   # (dividend: BFLOAT or num_str, divisor: BFLOAT or num_str) return 
   # (BFLOAT,BFLOAT) (quo,rem) or BFLOAT (only rem)
-  my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+
+  # set up parameters
+  my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+    }
 
   return $self->_div_inf($x,$y)
    if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
@@ -872,7 +920,6 @@ sub bdiv
     # promote BigInts and it's subclasses (except when already a BigFloat)
     $y = $self->new($y) unless $y->isa('Math::BigFloat'); 
 
-    #print "bdiv $y ",ref($y),"\n";
     # need to disable $upgrade in BigInt, to avoid deep recursion
     local $Math::BigInt::upgrade = undef;      # should be parent class vs MBI
 
@@ -888,10 +935,12 @@ sub bdiv
   # shortcut to not run trough _find_round_parameters again
   if (defined $params[1])
     {
+    $x->{_a} = undef;                          # clear before round
     $x->bround($params[1],$params[3]);         # then round accordingly
     }
   else
     {
+    $x->{_p} = undef;                          # clear before round
     $x->bfround($params[2],$params[3]);                # then round accordingly
     }
   if ($fallback)
@@ -923,12 +972,22 @@ sub bdiv
 sub bmod 
   {
   # (dividend: BFLOAT or num_str, divisor: BFLOAT or num_str) return reminder 
-  my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+
+  # set up parameters
+  my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+    }
 
   if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
     {
     my ($d,$re) = $self->SUPER::_div_inf($x,$y);
-    return $re->round($a,$p,$r,$y);
+    $x->{sign} = $re->{sign};
+    $x->{_e} = $re->{_e};
+    $x->{_m} = $re->{_m};
+    return $x->round($a,$p,$r,$y);
     } 
   return $x->bnan() if $x->is_zero() && $y->is_zero();
   return $x if $y->is_zero();
@@ -1013,7 +1072,8 @@ sub bsqrt
   my @params = $x->_find_round_parameters($a,$p,$r);
 
   # no rounding at all, so must use fallback
-  if (scalar @params == 1)
+  if ((scalar @params == 1) ||
+      (!defined($params[1] || $params[2])))
     {
     # simulate old behaviour
     $params[1] = $self->div_scale();   # and round to it as accuracy
@@ -1025,7 +1085,7 @@ sub bsqrt
     {
     # the 4 below is empirical, and there might be cases where it is not
     # enough...
-    $scale = abs($params[1] || $params[2]) + 4;        # take whatever is defined
+    $scale = abs($params[1] || $params[2]) + 4; # take whatever is defined
     }
 
   # when user set globals, they would interfere with our calculation, so
@@ -1042,7 +1102,6 @@ sub bsqrt
   my $xas = $x->as_number();
   my $gs = $xas->copy()->bsqrt();      # some guess
 
-#  print "guess $gs\n";
   if (($x->{_e}->{sign} ne '-')                # guess can't be accurate if there are
                                        # digits after the dot
    && ($xas->bacmp($gs * $gs) == 0))   # guess hit the nail on the head?
@@ -1067,29 +1126,37 @@ sub bsqrt
     ${"$self\::accuracy"} = $ab; ${"$self\::precision"} = $pb;
     return $x;
     }
-  $gs = $self->new( $gs );             # BigInt to BigFloat
-
-  my $lx = $x->{_m}->length();
-  $scale = $lx if $scale < $lx;
-  my $e = $self->new("1E-$scale");     # make test variable
-
-  my $y = $x->copy();
-  my $two = $self->new(2);
-  my $diff = $e;
-  # promote BigInts and it's subclasses (except when already a BigFloat)
-  $y = $self->new($y) unless $y->isa('Math::BigFloat'); 
+  # sqrt(2) = 1.4 because sqrt(2*100) = 1.4*10; so we can increase the accuracy
+  # of the result by multipyling the input by 100 and then divide the integer
+  # result of sqrt(input) by 10. Rounding afterwards returns the real result.
+  # this will transform 123.456 (in $x) into 123456 (in $y1)
+  my $y1 = $x->{_m}->copy();
+  # We now make sure that $y1 has the same odd or even number of digits than
+  # $x had. So when _e of $x is odd, we must shift $y1 by one digit left,
+  # because we always must multiply by steps of 100 (sqrt(100) is 10) and not
+  # steps of 10. The length of $x does not count, since an even or odd number
+  # of digits before the dot is not changed by adding an even number of digits
+  # after the dot (the result is still odd or even digits long).
+  $y1->bmul(10) if $x->{_e}->is_odd();
+  # now calculate how many digits the result of sqrt(y1) would have
+  my $digits = int($y1->length() / 2);
+  # but we need at least $scale digits, so calculate how many are missing
+  my $shift = $scale - $digits;
+  # that should never happen (we take care of integer guesses above)
+  # $shift = 0 if $shift < 0; 
+  # multiply in steps of 100, by shifting left two times the "missing" digits
+  $y1->blsft($shift*2,10);
+  # now take the square root and truncate to integer
+  $y1->bsqrt();
+  # By "shifting" $y1 right (by creating a negative _e) we calculate the final
+  # result, which is than later rounded to the desired scale.
+  $x->{_m} = $y1;
+  # gs->length() is the number of digits before the dot. Since gs is always
+  # truncated (9.99 => 9), it is always right (if gs was rounded, it would be
+  # '10' and thus gs->length() == 2, which would be wrong).
+  $x->{_e} = $MBI->new(- $y1->length() + $gs->length());
 
-  my $rem;
-  while ($diff->bacmp($e) >= 0)
-    {
-    $rem = $y->copy()->bdiv($gs,$scale);
-    $rem = $y->copy()->bdiv($gs,$scale)->badd($gs)->bdiv($two,$scale);
-    $diff = $rem->copy()->bsub($gs);
-    $gs = $rem->copy();
-    }
-  # copy over to modify $x
-  $x->{_m} = $rem->{_m}; $x->{_e} = $rem->{_e};
-  
   # shortcut to not run trough _find_round_parameters again
   if (defined $params[1])
     {
@@ -1120,7 +1187,7 @@ sub bfac
     if (($x->{sign} ne '+') ||         # inf, NaN, <0 etc => NaN
      ($x->{_e}->{sign} ne '+'));       # digits after dot?
 
-  return $x->bone(@r) if $x->is_zero() || $x->is_one();                # 0 or 1 => 1
+  return $x->bone('+',@r) if $x->is_zero() || $x->is_one();    # 0 or 1 => 1
   
   # use BigInt's bfac() for faster calc
   $x->{_m}->blsft($x->{_e},10);                # un-norm m
@@ -1157,7 +1224,7 @@ sub _pow2
     }
 
   # when user set globals, they would interfere with our calculation, so
-  # disable then and later re-enable them
+  # disable them and later re-enable them
   no strict 'refs';
   my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
   my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
@@ -1267,7 +1334,7 @@ sub _pow
     }
 
   # when user set globals, they would interfere with our calculation, so
-  # disable then and later re-enable them
+  # disable them and later re-enable them
   no strict 'refs';
   my $abr = "$self\::accuracy"; my $ab = $$abr; $$abr = undef;
   my $pbr = "$self\::precision"; my $pb = $$pbr; $$pbr = undef;
@@ -1328,7 +1395,13 @@ sub bpow
   # compute power of two numbers, second arg is used as integer
   # modifies first argument
 
-  my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+  # set up parameters
+  my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+    }
 
   return $x if $x->{sign} =~ /^[+-]inf$/;
   return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
@@ -1388,7 +1461,6 @@ sub bfround
     return $x; 
     }
   return $x if $x->{sign} !~ /^[+-]$/;
-  # print "MBF bfround $x to scale $scale mode $mode\n";
 
   # don't round if x already has lower precision
   return $x if (defined $x->{_p} && $x->{_p} < 0 && $scale < $x->{_p});
@@ -1397,16 +1469,20 @@ sub bfround
   $x->{_a} = undef;                    # and clear A
   if ($scale < 0)
     {
-    # print "bfround scale $scale e $x->{_e}\n";
     # round right from the '.'
-    return $x if $x->{_e} >= 0;                        # nothing to round
+
+    return $x if $x->{_e}->{sign} eq '+';      # e >= 0 => nothing to round
+
     $scale = -$scale;                          # positive for simplicity
     my $len = $x->{_m}->length();              # length of mantissa
-    my $dad = -$x->{_e};                       # digits after dot
+
+    # the following poses a restriction on _e, but if _e is bigger than a
+    # scalar, you got other problems (memory etc) anyway
+    my $dad = -($x->{_e}->numify());           # digits after dot
     my $zad = 0;                               # zeros after dot
-    $zad = -$len-$x->{_e} if ($x->{_e} < -$len);# for 0.00..00xxx style
+    $zad = $dad - $len if (-$dad < -$len);     # for 0.00..00xxx style
+    
     #print "scale $scale dad $dad zad $zad len $len\n";
-
     # number  bsstr   len zad dad      
     # 0.123   123e-3   3   0 3
     # 0.0123  123e-4   3   1 4
@@ -1437,15 +1513,16 @@ sub bfround
        $scale = $dbd+$scale;
         }
       }
-    # print "round to $x->{_m} to $scale\n";
     }
   else
     {
+    # round left from the '.'
+
     # 123 => 100 means length(123) = 3 - $scale (2) => 1
 
     my $dbt = $x->{_m}->length(); 
     # digits before dot 
-    my $dbd = $dbt + $x->{_e}; 
+    my $dbd = $dbt + $x->{_e}->numify(); 
     # should be the same, so treat it as this 
     $scale = 1 if $scale == 0; 
     # shortcut if already integer 
@@ -1467,9 +1544,7 @@ sub bfround
        { 
        $scale = $dbd - $scale; 
        }
-
     }
-  # print "using $scale for $x->{_m} with '$mode'\n";
   # pass sign to bround for rounding modes '+inf' and '-inf'
   $x->{_m}->{sign} = $x->{sign};
   $x->{_m}->bround($scale,$mode);
@@ -1530,10 +1605,6 @@ sub bfloor
   # if $x has digits after dot
   if ($x->{_e}->{sign} eq '-')
     {
-    #$x->{_m}->brsft(-$x->{_e},10);
-    #$x->{_e}->bzero();
-    #$x-- if $x->{sign} eq '-';
-
     $x->{_e}->{sign} = '+';                    # negate e
     $x->{_m}->brsft($x->{_e},10);              # cut off digits after dot
     $x->{_e}->bzero();                         # trunc/norm    
@@ -1567,26 +1638,40 @@ sub bceil
 
 sub brsft
   {
-  # shift right by $y (divide by power of 2)
-  my ($self,$x,$y,$n,$a,$p,$r) = objectify(2,@_);
+  # shift right by $y (divide by power of $n)
+  
+  # set up parameters
+  my ($self,$x,$y,$n,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$n,$a,$p,$r) = objectify(2,@_);
+    }
 
   return $x if $x->modify('brsft');
   return $x if $x->{sign} !~ /^[+-]$/; # nan, +inf, -inf
 
-  $n = 2 if !defined $n; $n = Math::BigFloat->new($n);
-  $x->bdiv($n ** $y,$a,$p,$r,$y);
+  $n = 2 if !defined $n; $n = $self->new($n);
+  $x->bdiv($n->bpow($y),$a,$p,$r,$y);
   }
 
 sub blsft
   {
-  # shift right by $y (divide by power of 2)
-  my ($self,$x,$y,$n,$a,$p,$r) = objectify(2,@_);
+  # shift left by $y (multiply by power of $n)
+  
+  # set up parameters
+  my ($self,$x,$y,$n,$a,$p,$r) = (ref($_[0]),@_);
+  # objectify is costly, so avoid it
+  if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+    {
+    ($self,$x,$y,$n,$a,$p,$r) = objectify(2,@_);
+    }
 
-  return $x if $x->modify('brsft');
+  return $x if $x->modify('blsft');
   return $x if $x->{sign} !~ /^[+-]$/; # nan, +inf, -inf
 
-  $n = 2 if !defined $n; $n = Math::BigFloat->new($n);
-  $x->bmul($n ** $y,$a,$p,$r,$y);
+  $n = 2 if !defined $n; $n = $self->new($n);
+  $x->bmul($n->bpow($y),$a,$p,$r,$y);
   }
 
 ###############################################################################
@@ -1681,7 +1766,6 @@ sub import
   my $lib = ''; my @a;
   for ( my $i = 0; $i < $l ; $i++)
     {
-#    print "at $_[$i] (",$_[$i+1]||'undef',")\n";
     if ( $_[$i] eq ':constant' )
       {
       # this rest causes overlord er load to step in
@@ -1781,7 +1865,44 @@ sub bnorm
   } 
  
 ##############################################################################
-# internal calculation routines
+
+sub as_hex
+  {
+  # return number as hexadecimal string (only for integers defined)
+  my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+
+  return $x->bstr() if $x->{sign} !~ /^[+-]$/;  # inf, nan etc
+  return '0x0' if $x->is_zero();
+
+  return 'NaN' if $x->{_e}->{sign} ne '+';     # how to do 1e-1 in hex!?
+
+  my $z = $x->{_m}->copy();
+  if (!$x->{_e}->is_zero())            # > 0 
+    {
+    $z->blsft($x->{_e},10);
+    }
+  $z->{sign} = $x->{sign};
+  $z->as_hex();
+  }
+
+sub as_bin
+  {
+  # return number as binary digit string (only for integers defined)
+  my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+
+  return $x->bstr() if $x->{sign} !~ /^[+-]$/;  # inf, nan etc
+  return '0b0' if $x->is_zero();
+
+  return 'NaN' if $x->{_e}->{sign} ne '+';     # how to do 1e-1 in hex!?
+
+  my $z = $x->{_m}->copy();
+  if (!$x->{_e}->is_zero())            # > 0 
+    {
+    $z->blsft($x->{_e},10);
+    }
+  $z->{sign} = $x->{sign};
+  $z->as_bin();
+  }
 
 sub as_number
   {
@@ -1918,6 +2039,14 @@ Math::BigFloat - Arbitrary size floating point math package
   $x->length();                        # number of digits (w/o sign and '.')
   ($l,$f) = $x->length();      # number of digits, and length of fraction      
 
+  $x->precision();             # return P of $x (or global, if P of $x undef)
+  $x->precision($n);           # set P of $x to $n
+  $x->accuracy();              # return A of $x (or global, if A of $x undef)
+  $x->accuracy($n);            # set A $x to $n
+
+  Math::BigFloat->precision(); # get/set global P for all BigFloat objects
+  Math::BigFloat->accuracy();  # get/set global A for all BigFloat objects
+
 =head1 DESCRIPTION
 
 All operators (inlcuding basic math operations) are overloaded if you