* provide implementation of arithmetic operators and numerical evaluation
* of special functions or implement the interface to the bignum package. */
+/*
+ * GiNaC Copyright (C) 1999 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
#include <vector>
#include <stdexcept>
-#include "ginac.h"
+#include "numeric.h"
+#include "ex.h"
+#include "config.h"
+#include "debugmsg.h"
// CLN should not pollute the global namespace, hence we include it here
// instead of in some header file where it would propagate to other parts:
#include <cln.h>
#endif
+namespace GiNaC {
+
// linker has no problems finding text symbols for numerator or denominator
//#define SANE_LINKER
// public
/** default ctor. Numerically it initializes to an integer zero. */
-numeric::numeric() : basic(TINFO_NUMERIC)
+numeric::numeric() : basic(TINFO_numeric)
{
debugmsg("numeric default constructor", LOGLEVEL_CONSTRUCT);
value = new cl_N;
// public
-numeric::numeric(int i) : basic(TINFO_NUMERIC)
+numeric::numeric(int i) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from int",LOGLEVEL_CONSTRUCT);
// Not the whole int-range is available if we don't cast to long
status_flags::hash_calculated);
}
-numeric::numeric(unsigned int i) : basic(TINFO_NUMERIC)
+numeric::numeric(unsigned int i) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from uint",LOGLEVEL_CONSTRUCT);
// Not the whole uint-range is available if we don't cast to ulong
status_flags::hash_calculated);
}
-numeric::numeric(long i) : basic(TINFO_NUMERIC)
+numeric::numeric(long i) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from long",LOGLEVEL_CONSTRUCT);
value = new cl_I(i);
status_flags::hash_calculated);
}
-numeric::numeric(unsigned long i) : basic(TINFO_NUMERIC)
+numeric::numeric(unsigned long i) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from ulong",LOGLEVEL_CONSTRUCT);
value = new cl_I(i);
/** Ctor for rational numerics a/b.
*
* @exception overflow_error (division by zero) */
-numeric::numeric(long numer, long denom) : basic(TINFO_NUMERIC)
+numeric::numeric(long numer, long denom) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from long/long",LOGLEVEL_CONSTRUCT);
if (!denom)
status_flags::hash_calculated);
}
-numeric::numeric(double d) : basic(TINFO_NUMERIC)
+numeric::numeric(double d) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from double",LOGLEVEL_CONSTRUCT);
// We really want to explicitly use the type cl_LF instead of the
status_flags::hash_calculated);
}
-numeric::numeric(char const *s) : basic(TINFO_NUMERIC)
+numeric::numeric(char const *s) : basic(TINFO_numeric)
{ // MISSING: treatment of complex and ints and rationals.
debugmsg("numeric constructor from string",LOGLEVEL_CONSTRUCT);
if (strchr(s, '.'))
/** Ctor from CLN types. This is for the initiated user or internal use
* only. */
-numeric::numeric(cl_N const & z) : basic(TINFO_NUMERIC)
+numeric::numeric(cl_N const & z) : basic(TINFO_numeric)
{
debugmsg("numeric constructor from cl_N", LOGLEVEL_CONSTRUCT);
value = new cl_N(z);
void numeric::print(ostream & os, unsigned upper_precedence) const
{
debugmsg("numeric print", LOGLEVEL_PRINT);
- if (is_real()) {
+ if (is_real()) {
// case 1, real: x or -x
if ((precedence<=upper_precedence) && (!is_pos_integer())) {
os << "(" << *value << ")";
int numeric::compare_same_type(basic const & other) const
{
- ASSERT(is_exactly_of_type(other, numeric));
+ GINAC_ASSERT(is_exactly_of_type(other, numeric));
numeric const & o = static_cast<numeric &>(const_cast<basic &>(other));
if (*value == *o.value) {
bool numeric::is_equal_same_type(basic const & other) const
{
- ASSERT(is_exactly_of_type(other,numeric));
+ GINAC_ASSERT(is_exactly_of_type(other,numeric));
numeric const *o = static_cast<numeric const *>(&other);
return is_equal(*o);
return operator=(numeric(s));
}
+/** Return the complex half-plane (left or right) in which the number lies.
+ * csgn(x)==0 for x==0, csgn(x)==1 for Re(x)>0 or Re(x)=0 and Im(x)>0,
+ * csgn(x)==-1 for Re(x)<0 or Re(x)=0 and Im(x)<0.
+ *
+ * @see numeric::compare(numeric const & other) */
+int numeric::csgn(void) const
+{
+ if (is_zero())
+ return 0;
+ if (!zerop(realpart(*value))) {
+ if (plusp(realpart(*value)))
+ return 1;
+ else
+ return -1;
+ } else {
+ if (plusp(imagpart(*value)))
+ return 1;
+ else
+ return -1;
+ }
+}
+
/** This method establishes a canonical order on all numbers. For complex
* numbers this is not possible in a mathematically consistent way but we need
* to establish some order and it ought to be fast. So we simply define it
- * similar to Maple's csgn. */
+ * to be compatible with our method csgn.
+ *
+ * @return csgn(*this-other)
+ * @see numeric::csgn(void) */
int numeric::compare(numeric const & other) const
{
// Comparing two real numbers?
* if the number is really an integer before calling this method. */
int numeric::to_int(void) const
{
- ASSERT(is_integer());
+ GINAC_ASSERT(is_integer());
return cl_I_to_int(The(cl_I)(*value));
}
* if the number is really not complex before calling this method. */
double numeric::to_double(void) const
{
- ASSERT(is_real());
+ GINAC_ASSERT(is_real());
return cl_double_approx(realpart(*value));
}
/** Numerator. Computes the numerator of rational numbers, rationalized
* numerator of complex if real and imaginary part are both rational numbers
- * (i.e numer(4/3+5/6*I) == 8+5*I), the number itself in all other cases. */
+ * (i.e numer(4/3+5/6*I) == 8+5*I), the number carrying the sign in all other
+ * cases. */
numeric numeric::numer(void) const
{
if (is_integer()) {
type_info const & typeid_numeric=typeid(some_numeric);
/** Imaginary unit. This is not a constant but a numeric since we are
* natively handing complex numbers anyways. */
-const numeric I = (complex(cl_I(0),cl_I(1)));
+const numeric I = numeric(complex(cl_I(0),cl_I(1)));
//////////
// global functions
* @return arbitrary precision numerical exp(x). */
numeric exp(numeric const & x)
{
- return exp(*x.value); // -> CLN
+ return ::exp(*x.value); // -> CLN
}
/** Natural logarithm.
{
if (z.is_zero())
throw (std::overflow_error("log(): logarithmic singularity"));
- return log(*z.value); // -> CLN
+ return ::log(*z.value); // -> CLN
}
/** Numeric sine (trigonometric function).
* @return arbitrary precision numerical sin(x). */
numeric sin(numeric const & x)
{
- return sin(*x.value); // -> CLN
+ return ::sin(*x.value); // -> CLN
}
/** Numeric cosine (trigonometric function).
* @return arbitrary precision numerical cos(x). */
numeric cos(numeric const & x)
{
- return cos(*x.value); // -> CLN
+ return ::cos(*x.value); // -> CLN
}
/** Numeric tangent (trigonometric function).
* @return arbitrary precision numerical tan(x). */
numeric tan(numeric const & x)
{
- return tan(*x.value); // -> CLN
+ return ::tan(*x.value); // -> CLN
}
/** Numeric inverse sine (trigonometric function).
* @return arbitrary precision numerical asin(x). */
numeric asin(numeric const & x)
{
- return asin(*x.value); // -> CLN
+ return ::asin(*x.value); // -> CLN
}
/** Numeric inverse cosine (trigonometric function).
* @return arbitrary precision numerical acos(x). */
numeric acos(numeric const & x)
{
- return acos(*x.value); // -> CLN
+ return ::acos(*x.value); // -> CLN
}
/** Arcustangents.
x.real().is_zero() &&
!abs(x.imag()).is_equal(numONE()))
throw (std::overflow_error("atan(): logarithmic singularity"));
- return atan(*x.value); // -> CLN
+ return ::atan(*x.value); // -> CLN
}
/** Arcustangents.
numeric atan(numeric const & y, numeric const & x)
{
if (x.is_real() && y.is_real())
- return atan(realpart(*x.value), realpart(*y.value)); // -> CLN
+ return ::atan(realpart(*x.value), realpart(*y.value)); // -> CLN
else
throw (std::invalid_argument("numeric::atan(): complex argument"));
}
* @return arbitrary precision numerical sinh(x). */
numeric sinh(numeric const & x)
{
- return sinh(*x.value); // -> CLN
+ return ::sinh(*x.value); // -> CLN
}
/** Numeric hyperbolic cosine (trigonometric function).
* @return arbitrary precision numerical cosh(x). */
numeric cosh(numeric const & x)
{
- return cosh(*x.value); // -> CLN
+ return ::cosh(*x.value); // -> CLN
}
/** Numeric hyperbolic tangent (trigonometric function).
* @return arbitrary precision numerical tanh(x). */
numeric tanh(numeric const & x)
{
- return tanh(*x.value); // -> CLN
+ return ::tanh(*x.value); // -> CLN
}
/** Numeric inverse hyperbolic sine (trigonometric function).
* @return arbitrary precision numerical asinh(x). */
numeric asinh(numeric const & x)
{
- return asinh(*x.value); // -> CLN
+ return ::asinh(*x.value); // -> CLN
}
/** Numeric inverse hyperbolic cosine (trigonometric function).
* @return arbitrary precision numerical acosh(x). */
numeric acosh(numeric const & x)
{
- return acosh(*x.value); // -> CLN
+ return ::acosh(*x.value); // -> CLN
}
/** Numeric inverse hyperbolic tangent (trigonometric function).
* @return arbitrary precision numerical atanh(x). */
numeric atanh(numeric const & x)
{
- return atanh(*x.value); // -> CLN
+ return ::atanh(*x.value); // -> CLN
+}
+
+/** Numeric evaluation of Riemann's Zeta function. Currently works only for
+ * integer arguments. */
+numeric zeta(numeric const & x)
+{
+ if (x.is_integer())
+ return ::cl_zeta(x.to_int()); // -> CLN
+ else
+ clog << "zeta(): Does anybody know good way to calculate this numerically?" << endl;
+ return numeric(0);
}
/** The gamma function.
- * stub stub stub stub stub stub! */
+ * This is only a stub! */
numeric gamma(numeric const & x)
{
- clog << "gamma(): Nobody expects the Spanish inquisition" << endl;
+ clog << "gamma(): Does anybody know good way to calculate this numerically?" << endl;
+ return numeric(0);
+}
+
+/** The psi function (aka polygamma function).
+ * This is only a stub! */
+numeric psi(numeric const & x)
+{
+ clog << "psi(): Does anybody know good way to calculate this numerically?" << endl;
+ return numeric(0);
+}
+
+/** The psi functions (aka polygamma functions).
+ * This is only a stub! */
+numeric psi(numeric const & n, numeric const & x)
+{
+ clog << "psi(): Does anybody know good way to calculate this numerically?" << endl;
return numeric(0);
}
throw (std::range_error("numeric::factorial(): argument must be integer >= 0"));
}
- return numeric(factorial(nn.to_int())); // -> CLN
+ return numeric(::factorial(nn.to_int())); // -> CLN
}
/** The double factorial combinatorial function. (Scarcely used, but still
* @exception range_error (argument must be integer >= -1) */
numeric doublefactorial(numeric const & nn)
{
+ // META-NOTE: The whole shit here will become obsolete and may be moved
+ // out once CLN learns about double factorial, which should be as soon as
+ // 1.0.3 rolls out!
+
// We store the results separately for even and odd arguments. This has
// the advantage that we don't have to compute any even result at all if
// the function is always called with odd arguments and vice versa. There
static vector<numeric> oddresults;
static int highest_oddresult = -1;
- if ( nn == numeric(-1) ) {
+ if (nn == numeric(-1)) {
return numONE();
}
- if ( !nn.is_nonneg_integer() ) {
+ if (!nn.is_nonneg_integer()) {
throw (std::range_error("numeric::doublefactorial(): argument must be integer >= -1"));
}
- if ( nn.is_even() ) {
+ if (nn.is_even()) {
int n = nn.div(numTWO()).to_int();
- if ( n <= highest_evenresult ) {
+ if (n <= highest_evenresult) {
return evenresults[n];
}
- if ( evenresults.capacity() < (unsigned)(n+1) ) {
+ if (evenresults.capacity() < (unsigned)(n+1)) {
evenresults.reserve(n+1);
}
- if ( highest_evenresult < 0 ) {
+ if (highest_evenresult < 0) {
evenresults.push_back(numONE());
highest_evenresult=0;
}
return evenresults[n];
} else {
int n = nn.sub(numONE()).div(numTWO()).to_int();
- if ( n <= highest_oddresult ) {
+ if (n <= highest_oddresult) {
return oddresults[n];
}
- if ( oddresults.capacity() < (unsigned)n ) {
+ if (oddresults.capacity() < (unsigned)n) {
oddresults.reserve(n+1);
}
- if ( highest_oddresult < 0 ) {
+ if (highest_oddresult < 0) {
oddresults.push_back(numONE());
highest_oddresult=0;
}
}
}
-/** The Binomial function. It computes the binomial coefficients. If the
- * arguments are both nonnegative integers and 0 <= k <= n, then
- * binomial(n, k) = n!/k!/(n-k)! which is the number of ways of choosing k
- * objects from n distinct objects. If k > n, then binomial(n,k) returns 0. */
+/** The Binomial coefficients. It computes the binomial coefficients. For
+ * integer n and k and positive n this is the number of ways of choosing k
+ * objects from n distinct objects. If n is negative, the formula
+ * binomial(n,k) == (-1)^k*binomial(k-n-1,k) is used to compute the result. */
numeric binomial(numeric const & n, numeric const & k)
{
- if (n.is_nonneg_integer() && k.is_nonneg_integer()) {
- return numeric(binomial(n.to_int(),k.to_int())); // -> CLN
- } else {
- // should really be gamma(n+1)/(gamma(r+1)/gamma(n-r+1)
- return numeric(0);
+ if (n.is_integer() && k.is_integer()) {
+ if (n.is_nonneg_integer()) {
+ if (k.compare(n)!=1 && k.compare(numZERO())!=-1)
+ return numeric(::binomial(n.to_int(),k.to_int())); // -> CLN
+ else
+ return numZERO();
+ } else {
+ return numMINUSONE().power(k)*binomial(k-n-numONE(),k);
+ }
}
- // return factorial(n).div(factorial(k).mul(factorial(n.sub(k))));
+
+ // should really be gamma(n+1)/(gamma(r+1)/gamma(n-r+1) or a suitable limit
+ throw (std::range_error("numeric::binomial(): donĀ“t know how to evaluate that."));
+}
+
+/** Bernoulli number. The nth Bernoulli number is the coefficient of x^n/n!
+ * in the expansion of the function x/(e^x-1).
+ *
+ * @return the nth Bernoulli number (a rational number).
+ * @exception range_error (argument must be integer >= 0) */
+numeric bernoulli(numeric const & nn)
+{
+ if (!nn.is_integer() || nn.is_negative())
+ throw (std::range_error("numeric::bernoulli(): argument must be integer >= 0"));
+ if (nn.is_zero())
+ return numONE();
+ if (!nn.compare(numONE()))
+ return numeric(-1,2);
+ if (nn.is_odd())
+ return numZERO();
+ // Until somebody has the Blues and comes up with a much better idea and
+ // codes it (preferably in CLN) we make this a remembering function which
+ // computes its results using the formula
+ // B(nn) == - 1/(nn+1) * sum_{k=0}^{nn-1}(binomial(nn+1,k)*B(k))
+ // whith B(0) == 1.
+ static vector<numeric> results;
+ static int highest_result = -1;
+ int n = nn.sub(numTWO()).div(numTWO()).to_int();
+ if (n <= highest_result)
+ return results[n];
+ if (results.capacity() < (unsigned)(n+1))
+ results.reserve(n+1);
+
+ numeric tmp; // used to store the sum
+ for (int i=highest_result+1; i<=n; ++i) {
+ // the first two elements:
+ tmp = numeric(-2*i-1,2);
+ // accumulate the remaining elements:
+ for (int j=0; j<i; ++j)
+ tmp += binomial(numeric(2*i+3),numeric(j*2+2))*results[j];
+ // divide by -(nn+1) and store result:
+ results.push_back(-tmp/numeric(2*i+3));
+ }
+ highest_result=n;
+ return results[n];
}
/** Absolute value. */
numeric abs(numeric const & x)
{
- return abs(*x.value); // -> CLN
+ return ::abs(*x.value); // -> CLN
}
/** Modulus (in positive representation).
numeric mod(numeric const & a, numeric const & b)
{
if (a.is_integer() && b.is_integer()) {
- return mod(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
+ return ::mod(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
}
else {
return numZERO(); // Throw?
{
if (a.is_integer() && b.is_integer()) {
cl_I b2 = The(cl_I)(ceiling1(The(cl_I)(*b.value) / 2)) - 1;
- return mod(The(cl_I)(*a.value) + b2, The(cl_I)(*b.value)) - b2;
+ return ::mod(The(cl_I)(*a.value) + b2, The(cl_I)(*b.value)) - b2;
} else {
return numZERO(); // Throw?
}
numeric irem(numeric const & a, numeric const & b)
{
if (a.is_integer() && b.is_integer()) {
- return rem(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
+ return ::rem(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
}
else {
return numZERO(); // Throw?
* where imag(z)>0. */
numeric sqrt(numeric const & z)
{
- return sqrt(*z.value); // -> CLN
+ return ::sqrt(*z.value); // -> CLN
}
/** Integer numeric square root. */
{
if (x.is_integer()) {
cl_I root;
- isqrt(The(cl_I)(*x.value), &root); // -> CLN
+ ::isqrt(The(cl_I)(*x.value), &root); // -> CLN
return root;
} else
return numZERO(); // Throw?
numeric gcd(numeric const & a, numeric const & b)
{
if (a.is_integer() && b.is_integer())
- return gcd(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
+ return ::gcd(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
else
return numONE();
}
numeric lcm(numeric const & a, numeric const & b)
{
if (a.is_integer() && b.is_integer())
- return lcm(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
+ return ::lcm(The(cl_I)(*a.value), The(cl_I)(*b.value)); // -> CLN
else
return *a.value * *b.value;
}
/** Accuracy in decimal digits. Only object of this type! Can be set using
* assignment from C++ unsigned ints and evaluated like any built-in type. */
_numeric_digits Digits;
+
+} // namespace GiNaC