]> www.ginac.de Git - ginac.git/blobdiff - ginac/numeric.cpp
tgamma, lgamma: convert to take cl_N as an argument; provide wrappers for GiNaC:...
[ginac.git] / ginac / numeric.cpp
index e4b1f22942d7e4e86ec242bcd2fcd2a2b1d719ca..40aa8e2ee90ec1b18e10a0e75a6d0084de81c81b 100644 (file)
@@ -7,7 +7,7 @@
  *  of special functions or implement the interface to the bignum package. */
 
 /*
- *  GiNaC Copyright (C) 1999-2007 Johannes Gutenberg University Mainz, Germany
+ *  GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -399,6 +399,40 @@ static void print_real_csrc(const print_context & c, const cln::cl_R & x)
        }
 }
 
+template<typename T1, typename T2> 
+static inline bool coerce(T1& dst, const T2& arg);
+
+/** 
+ * @brief Check if CLN integer can be converted into int
+ *
+ * @sa http://www.ginac.de/pipermail/cln-list/2006-October/000248.html
+ */
+template<>
+inline bool coerce<int, cln::cl_I>(int& dst, const cln::cl_I& arg)
+{
+       static const cln::cl_I cl_max_int = 
+               (cln::cl_I)(long)(std::numeric_limits<int>::max());
+       static const cln::cl_I cl_min_int =
+               (cln::cl_I)(long)(std::numeric_limits<int>::min());
+       if ((arg >= cl_min_int) && (arg <= cl_max_int)) {
+               dst = cl_I_to_int(arg);
+               return true;
+       }
+       return false;
+}
+
+template<>
+inline bool coerce<unsigned int, cln::cl_I>(unsigned int& dst, const cln::cl_I& arg)
+{
+       static const cln::cl_I cl_max_uint = 
+               (cln::cl_I)(unsigned long)(std::numeric_limits<unsigned int>::max());
+       if ((! minusp(arg)) && (arg <= cl_max_uint)) {
+               dst = cl_I_to_uint(arg);
+               return true;
+       }
+       return false;
+}
+
 /** Helper function to print real number in C++ source format using cl_N types.
  *
  *  @see numeric::print() */
@@ -406,11 +440,20 @@ static void print_real_cl_N(const print_context & c, const cln::cl_R & x)
 {
        if (cln::instanceof(x, cln::cl_I_ring)) {
 
-               // Integer number
-               c.s << "cln::cl_I(\"";
-               print_real_number(c, x);
-               c.s << "\")";
-
+    int dst;
+    // fixnum 
+    if (coerce(dst, cln::the<cln::cl_I>(x))) {
+      // can be converted to native int
+      if (dst < 0)
+        c.s << "(-" << dst << ")";
+      else
+        c.s << dst;
+    } else {
+      // bignum
+      c.s << "cln::cl_I(\"";
+      print_real_number(c, x);
+      c.s << "\")";
+    }
        } else if (cln::instanceof(x, cln::cl_RA_ring)) {
 
                // Rational number
@@ -576,6 +619,7 @@ bool numeric::info(unsigned inf) const
                case info_flags::numeric:
                case info_flags::polynomial:
                case info_flags::rational_function:
+               case info_flags::expanded:
                        return true;
                case info_flags::real:
                        return is_real();
@@ -1335,7 +1379,7 @@ const numeric I = numeric(cln::complex(cln::cl_I(0),cln::cl_I(1)));
  *  @return  arbitrary precision numerical exp(x). */
 const numeric exp(const numeric &x)
 {
-       return cln::exp(x.to_cl_N());
+       return numeric(cln::exp(x.to_cl_N()));
 }
 
 
@@ -1348,7 +1392,7 @@ const numeric log(const numeric &x)
 {
        if (x.is_zero())
                throw pole_error("log(): logarithmic pole",0);
-       return cln::log(x.to_cl_N());
+       return numeric(cln::log(x.to_cl_N()));
 }
 
 
@@ -1357,7 +1401,7 @@ const numeric log(const numeric &x)
  *  @return  arbitrary precision numerical sin(x). */
 const numeric sin(const numeric &x)
 {
-       return cln::sin(x.to_cl_N());
+       return numeric(cln::sin(x.to_cl_N()));
 }
 
 
@@ -1366,7 +1410,7 @@ const numeric sin(const numeric &x)
  *  @return  arbitrary precision numerical cos(x). */
 const numeric cos(const numeric &x)
 {
-       return cln::cos(x.to_cl_N());
+       return numeric(cln::cos(x.to_cl_N()));
 }
 
 
@@ -1375,7 +1419,7 @@ const numeric cos(const numeric &x)
  *  @return  arbitrary precision numerical tan(x). */
 const numeric tan(const numeric &x)
 {
-       return cln::tan(x.to_cl_N());
+       return numeric(cln::tan(x.to_cl_N()));
 }
        
 
@@ -1384,7 +1428,7 @@ const numeric tan(const numeric &x)
  *  @return  arbitrary precision numerical asin(x). */
 const numeric asin(const numeric &x)
 {
-       return cln::asin(x.to_cl_N());
+       return numeric(cln::asin(x.to_cl_N()));
 }
 
 
@@ -1393,37 +1437,55 @@ const numeric asin(const numeric &x)
  *  @return  arbitrary precision numerical acos(x). */
 const numeric acos(const numeric &x)
 {
-       return cln::acos(x.to_cl_N());
+       return numeric(cln::acos(x.to_cl_N()));
 }
        
 
-/** Arcustangent.
+/** Numeric arcustangent.
  *
  *  @param x complex number
  *  @return atan(x)
- *  @exception pole_error("atan(): logarithmic pole",0) */
+ *  @exception pole_error("atan(): logarithmic pole",0) if x==I or x==-I. */
 const numeric atan(const numeric &x)
 {
        if (!x.is_real() &&
            x.real().is_zero() &&
            abs(x.imag()).is_equal(*_num1_p))
                throw pole_error("atan(): logarithmic pole",0);
-       return cln::atan(x.to_cl_N());
+       return numeric(cln::atan(x.to_cl_N()));
 }
 
 
-/** Arcustangent.
+/** Numeric arcustangent of two arguments, analytically continued in a suitable way.
  *
- *  @param x real number
- *  @param y real number
- *  @return atan(y/x) */
+ *  @param y complex number
+ *  @param x complex number
+ *  @return -I*log((x+I*y)/sqrt(x^2+y^2)), which is equal to atan(y/x) if y and
+ *    x are both real.
+ *  @exception pole_error("atan(): logarithmic pole",0) if y/x==+I or y/x==-I. */
 const numeric atan(const numeric &y, const numeric &x)
 {
+       if (x.is_zero() && y.is_zero())
+               return *_num0_p;
        if (x.is_real() && y.is_real())
-               return cln::atan(cln::the<cln::cl_R>(x.to_cl_N()),
-                                cln::the<cln::cl_R>(y.to_cl_N()));
-       else
-               throw std::invalid_argument("atan(): complex argument");        
+               return numeric(cln::atan(cln::the<cln::cl_R>(x.to_cl_N()),
+                                cln::the<cln::cl_R>(y.to_cl_N())));
+
+       // Compute -I*log((x+I*y)/sqrt(x^2+y^2))
+       //      == -I*log((x+I*y)/sqrt((x+I*y)*(x-I*y)))
+       // Do not "simplify" this to -I/2*log((x+I*y)/(x-I*y))) or likewise.
+       // The branch cuts are easily messed up.
+       const cln::cl_N aux_p = x.to_cl_N()+cln::complex(0,1)*y.to_cl_N();
+       if (cln::zerop(aux_p)) {
+               // x+I*y==0 => y/x==I, so this is a pole (we have x!=0).
+               throw pole_error("atan(): logarithmic pole",0);
+       }
+       const cln::cl_N aux_m = x.to_cl_N()-cln::complex(0,1)*y.to_cl_N();
+       if (cln::zerop(aux_m)) {
+               // x-I*y==0 => y/x==-I, so this is a pole (we have x!=0).
+               throw pole_error("atan(): logarithmic pole",0);
+       }
+       return numeric(cln::complex(0,-1)*cln::log(aux_p/cln::sqrt(aux_p*aux_m)));
 }
 
 
@@ -1432,7 +1494,7 @@ const numeric atan(const numeric &y, const numeric &x)
  *  @return  arbitrary precision numerical sinh(x). */
 const numeric sinh(const numeric &x)
 {
-       return cln::sinh(x.to_cl_N());
+       return numeric(cln::sinh(x.to_cl_N()));
 }
 
 
@@ -1441,7 +1503,7 @@ const numeric sinh(const numeric &x)
  *  @return  arbitrary precision numerical cosh(x). */
 const numeric cosh(const numeric &x)
 {
-       return cln::cosh(x.to_cl_N());
+       return numeric(cln::cosh(x.to_cl_N()));
 }
 
 
@@ -1450,7 +1512,7 @@ const numeric cosh(const numeric &x)
  *  @return  arbitrary precision numerical tanh(x). */
 const numeric tanh(const numeric &x)
 {
-       return cln::tanh(x.to_cl_N());
+       return numeric(cln::tanh(x.to_cl_N()));
 }
        
 
@@ -1459,7 +1521,7 @@ const numeric tanh(const numeric &x)
  *  @return  arbitrary precision numerical asinh(x). */
 const numeric asinh(const numeric &x)
 {
-       return cln::asinh(x.to_cl_N());
+       return numeric(cln::asinh(x.to_cl_N()));
 }
 
 
@@ -1468,7 +1530,7 @@ const numeric asinh(const numeric &x)
  *  @return  arbitrary precision numerical acosh(x). */
 const numeric acosh(const numeric &x)
 {
-       return cln::acosh(x.to_cl_N());
+       return numeric(cln::acosh(x.to_cl_N()));
 }
 
 
@@ -1477,7 +1539,7 @@ const numeric acosh(const numeric &x)
  *  @return  arbitrary precision numerical atanh(x). */
 const numeric atanh(const numeric &x)
 {
-       return cln::atanh(x.to_cl_N());
+       return numeric(cln::atanh(x.to_cl_N()));
 }
 
 
@@ -1549,24 +1611,24 @@ static cln::cl_N Li2_projection(const cln::cl_N &x,
        return Li2_series(x, prec);
 }
 
+
 /** Numeric evaluation of Dilogarithm.  The domain is the entire complex plane,
  *  the branch cut lies along the positive real axis, starting at 1 and
  *  continuous with quadrant IV.
  *
  *  @return  arbitrary precision numerical Li2(x). */
-const numeric Li2(const numeric &x)
+const cln::cl_N Li2_(const cln::cl_N& value)
 {
-       if (x.is_zero())
-               return *_num0_p;
+       if (zerop(value))
+               return 0;
        
        // what is the desired float format?
        // first guess: default format
        cln::float_format_t prec = cln::default_float_format;
-       const cln::cl_N value = x.to_cl_N();
        // second guess: the argument's format
-       if (!x.real().is_rational())
+       if (!instanceof(realpart(value), cln::cl_RA_ring))
                prec = cln::float_format(cln::the<cln::cl_F>(cln::realpart(value)));
-       else if (!x.imag().is_rational())
+       else if (!instanceof(imagpart(value), cln::cl_RA_ring))
                prec = cln::float_format(cln::the<cln::cl_F>(cln::imagpart(value)));
        
        if (value==1)  // may cause trouble with log(1-x)
@@ -1578,7 +1640,16 @@ const numeric Li2(const numeric &x)
                       - cln::zeta(2, prec)
                       - Li2_projection(cln::recip(value), prec));
        else
-               return Li2_projection(x.to_cl_N(), prec);
+               return Li2_projection(value, prec);
+}
+
+const numeric Li2(const numeric &x)
+{
+       const cln::cl_N x_ = x.to_cl_N();
+       if (zerop(x_))
+               return *_num0_p;
+       const cln::cl_N result = Li2_(x_);
+       return numeric(result);
 }
 
 
@@ -1594,7 +1665,7 @@ const numeric zeta(const numeric &x)
        if (x.is_real()) {
                const int aux = (int)(cln::double_approx(cln::the<cln::cl_R>(x.to_cl_N())));
                if (cln::zerop(x.to_cl_N()-aux))
-                       return cln::zeta(aux);
+                       return numeric(cln::zeta(aux));
        }
        throw dunno();
 }
@@ -1891,24 +1962,34 @@ lanczos_coeffs::lanczos_coeffs()
        coeffs[3].swap(coeffs_120);
 }
 
+static const cln::float_format_t guess_precision(const cln::cl_N& x)
+{
+       cln::float_format_t prec = cln::default_float_format;
+       if (!instanceof(realpart(x), cln::cl_RA_ring))
+               prec = cln::float_format(cln::the<cln::cl_F>(realpart(x)));
+       if (!instanceof(imagpart(x), cln::cl_RA_ring))
+               prec = cln::float_format(cln::the<cln::cl_F>(imagpart(x)));
+       return prec;
+}
 
 /** The Gamma function.
  *  Use the Lanczos approximation. If the coefficients used here are not
  *  sufficiently many or sufficiently accurate, more can be calculated
  *  using the program doc/examples/lanczos.cpp. In that case, be sure to
  *  read the comments in that file. */
-const numeric lgamma(const numeric &x)
+const cln::cl_N lgamma_(const cln::cl_N &x)
 {
+       cln::float_format_t prec = guess_precision(x);
        lanczos_coeffs lc;
-       if (lc.sufficiently_accurate(Digits)) {
-               cln::cl_N pi_val = cln::pi(cln::default_float_format);
-               if (x.real() < 0.5)
-                       return log(pi_val) - log(sin(pi_val*x.to_cl_N()))
-                               - lgamma(numeric(1).sub(x));
-               cln::cl_N A = lc.calc_lanczos_A(x.to_cl_N());
-               cln::cl_N temp = x.to_cl_N() + lc.get_order() - cln::cl_N(1)/2;
+       if (lc.sufficiently_accurate(prec)) {
+               cln::cl_N pi_val = cln::pi(prec);
+               if (realpart(x) < 0.5)
+                       return cln::log(pi_val) - cln::log(sin(pi_val*x))
+                               - lgamma_(1 - x);
+               cln::cl_N A = lc.calc_lanczos_A(x);
+               cln::cl_N temp = x + lc.get_order() - cln::cl_N(1)/2;
        cln::cl_N result = log(cln::cl_I(2)*pi_val)/2
-                             + (x.to_cl_N()-cln::cl_N(1)/2)*log(temp)
+                             + (x-cln::cl_N(1)/2)*log(temp)
                              - temp
                              + log(A);
        return result;
@@ -1917,17 +1998,25 @@ const numeric lgamma(const numeric &x)
                throw dunno();
 }
 
-const numeric tgamma(const numeric &x)
+const numeric lgamma(const numeric &x)
 {
+       const cln::cl_N x_ = x.to_cl_N();
+       const cln::cl_N result = lgamma_(x_);
+       return numeric(result);
+}
+
+const cln::cl_N tgamma_(const cln::cl_N &x)
+{
+       cln::float_format_t prec = guess_precision(x);
        lanczos_coeffs lc;
-       if (lc.sufficiently_accurate(Digits)) {
-               cln::cl_N pi_val = cln::pi(cln::default_float_format);
-               if (x.real() < 0.5)
-                       return pi_val/(sin(pi_val*x))/(tgamma(numeric(1).sub(x)).to_cl_N());
-               cln::cl_N A = lc.calc_lanczos_A(x.to_cl_N());
-               cln::cl_N temp = x.to_cl_N() + lc.get_order() - cln::cl_N(1)/2;
+       if (lc.sufficiently_accurate(prec)) {
+               cln::cl_N pi_val = cln::pi(prec);
+               if (realpart(x) < 0.5)
+                       return pi_val/(cln::sin(pi_val*x))/tgamma_(1 - x);
+               cln::cl_N A = lc.calc_lanczos_A(x);
+               cln::cl_N temp = x + lc.get_order() - cln::cl_N(1)/2;
        cln::cl_N result
-                       = sqrt(cln::cl_I(2)*pi_val) * expt(temp, x.to_cl_N()-cln::cl_N(1)/2)
+                       = sqrt(cln::cl_I(2)*pi_val) * expt(temp, x - cln::cl_N(1)/2)
                          * exp(-temp) * A;
        return result;
        }
@@ -1935,6 +2024,12 @@ const numeric tgamma(const numeric &x)
                throw dunno();
 }
 
+const numeric tgamma(const numeric &x)
+{
+       const cln::cl_N x_ = x.to_cl_N();
+       const cln::cl_N result = tgamma_(x_);
+       return numeric(result);
+}
 
 /** The psi function (aka polygamma function).
  *  This is only a stub! */
@@ -2157,7 +2252,7 @@ const numeric fibonacci(const numeric &n)
 /** Absolute value. */
 const numeric abs(const numeric& x)
 {
-       return cln::abs(x.to_cl_N());
+       return numeric(cln::abs(x.to_cl_N()));
 }
 
 
@@ -2171,8 +2266,8 @@ const numeric abs(const numeric& x)
 const numeric mod(const numeric &a, const numeric &b)
 {
        if (a.is_integer() && b.is_integer())
-               return cln::mod(cln::the<cln::cl_I>(a.to_cl_N()),
-                               cln::the<cln::cl_I>(b.to_cl_N()));
+               return numeric(cln::mod(cln::the<cln::cl_I>(a.to_cl_N()),
+                               cln::the<cln::cl_I>(b.to_cl_N())));
        else
                return *_num0_p;
 }
@@ -2186,8 +2281,8 @@ const numeric smod(const numeric &a, const numeric &b)
 {
        if (a.is_integer() && b.is_integer()) {
                const cln::cl_I b2 = cln::ceiling1(cln::the<cln::cl_I>(b.to_cl_N()) >> 1) - 1;
-               return cln::mod(cln::the<cln::cl_I>(a.to_cl_N()) + b2,
-                               cln::the<cln::cl_I>(b.to_cl_N())) - b2;
+               return numeric(cln::mod(cln::the<cln::cl_I>(a.to_cl_N()) + b2,
+                               cln::the<cln::cl_I>(b.to_cl_N())) - b2);
        } else
                return *_num0_p;
 }
@@ -2205,8 +2300,8 @@ const numeric irem(const numeric &a, const numeric &b)
        if (b.is_zero())
                throw std::overflow_error("numeric::irem(): division by zero");
        if (a.is_integer() && b.is_integer())
-               return cln::rem(cln::the<cln::cl_I>(a.to_cl_N()),
-                               cln::the<cln::cl_I>(b.to_cl_N()));
+               return numeric(cln::rem(cln::the<cln::cl_I>(a.to_cl_N()),
+                               cln::the<cln::cl_I>(b.to_cl_N())));
        else
                return *_num0_p;
 }
@@ -2227,8 +2322,8 @@ const numeric irem(const numeric &a, const numeric &b, numeric &q)
        if (a.is_integer() && b.is_integer()) {
                const cln::cl_I_div_t rem_quo = cln::truncate2(cln::the<cln::cl_I>(a.to_cl_N()),
                                                               cln::the<cln::cl_I>(b.to_cl_N()));
-               q = rem_quo.quotient;
-               return rem_quo.remainder;
+               q = numeric(rem_quo.quotient);
+               return numeric(rem_quo.remainder);
        } else {
                q = *_num0_p;
                return *_num0_p;
@@ -2246,8 +2341,8 @@ const numeric iquo(const numeric &a, const numeric &b)
        if (b.is_zero())
                throw std::overflow_error("numeric::iquo(): division by zero");
        if (a.is_integer() && b.is_integer())
-               return cln::truncate1(cln::the<cln::cl_I>(a.to_cl_N()),
-                                 cln::the<cln::cl_I>(b.to_cl_N()));
+               return numeric(cln::truncate1(cln::the<cln::cl_I>(a.to_cl_N()),
+                                 cln::the<cln::cl_I>(b.to_cl_N())));
        else
                return *_num0_p;
 }
@@ -2267,7 +2362,7 @@ const numeric iquo(const numeric &a, const numeric &b, numeric &r)
        if (a.is_integer() && b.is_integer()) {
                const cln::cl_I_div_t rem_quo = cln::truncate2(cln::the<cln::cl_I>(a.to_cl_N()),
                                                               cln::the<cln::cl_I>(b.to_cl_N()));
-               r = rem_quo.remainder;
+               r = numeric(rem_quo.remainder);
                return rem_quo.quotient;
        } else {
                r = *_num0_p;
@@ -2283,8 +2378,8 @@ const numeric iquo(const numeric &a, const numeric &b, numeric &r)
 const numeric gcd(const numeric &a, const numeric &b)
 {
        if (a.is_integer() && b.is_integer())
-               return cln::gcd(cln::the<cln::cl_I>(a.to_cl_N()),
-                               cln::the<cln::cl_I>(b.to_cl_N()));
+               return numeric(cln::gcd(cln::the<cln::cl_I>(a.to_cl_N()),
+                               cln::the<cln::cl_I>(b.to_cl_N())));
        else
                return *_num1_p;
 }
@@ -2297,8 +2392,8 @@ const numeric gcd(const numeric &a, const numeric &b)
 const numeric lcm(const numeric &a, const numeric &b)
 {
        if (a.is_integer() && b.is_integer())
-               return cln::lcm(cln::the<cln::cl_I>(a.to_cl_N()),
-                               cln::the<cln::cl_I>(b.to_cl_N()));
+               return numeric(cln::lcm(cln::the<cln::cl_I>(a.to_cl_N()),
+                               cln::the<cln::cl_I>(b.to_cl_N())));
        else
                return a.mul(b);
 }
@@ -2314,7 +2409,7 @@ const numeric lcm(const numeric &a, const numeric &b)
  *  where imag(x)>0. */
 const numeric sqrt(const numeric &x)
 {
-       return cln::sqrt(x.to_cl_N());
+       return numeric(cln::sqrt(x.to_cl_N()));
 }
 
 
@@ -2324,7 +2419,7 @@ const numeric isqrt(const numeric &x)
        if (x.is_integer()) {
                cln::cl_I root;
                cln::isqrt(cln::the<cln::cl_I>(x.to_cl_N()), &root);
-               return root;
+               return numeric(root);
        } else
                return *_num0_p;
 }