/* mxNumber - Arbitrary precision numbers provided by the GNU MP library. Copyright (c) 2001-2002, eGenix.com Software GmbH; mailto:info@egenix.com See the documentation for further copyright information or contact the author (mailto:mal@lemburg.com). */ /* Define this to aid in finding memory leaks */ /*#define MAL_MEM_DEBUG*/ /*#define MAL_DEBUG*/ /* Logging file used by debugging facility */ #ifndef MAL_DEBUG_OUTPUTFILE # define MAL_DEBUG_OUTPUTFILE "mxNumber.log" #endif /* We want all our symbols to be exported */ #define MX_BUILDING_MXNUMBER #include "mx.h" #include "mxNumber.h" /* Check Python version */ #if PY_VERSION_HEX <= 0x02010000 # error "*** mxNumber needs Python >=2.1 to be installed !" #endif /* Version number: Major.Minor.Patchlevel */ #define MXNUMBER_VERSION "0.5.0" /* Define to have the module use free lists (saves malloc calls) */ #define MXNUMBER_FREELIST /* Define this to enable the copy-protocol (__copy__, __deepcopy__) */ #define COPY_PROTOCOL /* --- Module helpers --------------------------------------------------- */ /* --- Module doc-string ------------------------------------------------ */ static char *Module_docstring = MXNUMBER_MODULE" -- Arbitrary precision numbers provided by GNU MP. " "Version "MXNUMBER_VERSION"\n\n" "Copyright (c) 2001-2002, eGenix.com Software GmbH; mailto:info@egenix.com\n\n" " All Rights Reserved\n\n" "See the documentation for further information on copyrights,\n" "or contact the author." ; /* --- Module globals --------------------------------------------------- */ static PyObject *mxNumber_Error; /* Error Exception object */ /* Free lists for objects */ #ifdef MXNUMBER_FREELIST static mxIntegerObject *mxInteger_FreeList = NULL; static mxRationalObject *mxRational_FreeList = NULL; static mxFloatObject *mxFloat_FreeList = NULL; #endif /* Value range for signed longs (as mpz number) */ static mpz_t max_slong; static mpz_t min_slong; /* Default precision to use when creating mxFloats */ static int mxFloat_default_precision = 64; /* Flag telling us whether the module was initialized or not. */ static int mxNumber_Initialized = 0; /* --- Forward declarations --------------------------------------------- */ staticforward PyTypeObject mxInteger_Type; staticforward PyMethodDef mxInteger_Methods[]; staticforward PyTypeObject mxRational_Type; staticforward PyMethodDef mxRational_Methods[]; staticforward PyTypeObject mxFloat_Type; staticforward PyMethodDef mxFloat_Methods[]; /* Generic Number APIs */ staticforward PyObject *mxNumber_AsPyFloat(PyObject *value); staticforward PyObject *mxNumber_BinaryPyFloatOperation(binaryfunc binop, PyObject *left, PyObject *right); staticforward PyObject *mxNumber_TernaryPyFloatOperation(ternaryfunc ternop, PyObject *left, PyObject *right, PyObject *extra); staticforward PyObject *mxNumber_BinaryIntegerOperation(binaryfunc binop, PyObject *left, PyObject *right); staticforward PyObject *mxNumber_BinaryRationalOperation(binaryfunc binop, PyObject *left, PyObject *right); staticforward PyObject *mxNumber_BinaryFloatOperation(binaryfunc binop, PyObject *left, PyObject *right); staticforward PyObject *mxInteger_FromObject(PyObject *value); staticforward PyObject *mxRational_FromObject(PyObject *value); staticforward PyObject *mxFloat_FromObject(PyObject *value); /* --- Internal macros -------------------------------------------------- */ #define _mxInteger_Check(v) \ (((mxIntegerObject *)(v))->ob_type == &mxInteger_Type) #define _mxRational_Check(v) \ (((mxRationalObject *)(v))->ob_type == &mxRational_Type) #define _mxFloat_Check(v) \ (((mxFloatObject *)(v))->ob_type == &mxFloat_Type) /* --- Module helpers --------------------------------------------------- */ /* Create an exception object, insert it into the module dictionary under the given name and return the object pointer; this is NULL in case an error occurred. base can be given to indicate the base object to be used by the exception object. It should be NULL otherwise */ static PyObject *insexc(PyObject *moddict, char *name, PyObject *base) { PyObject *v; char fullname[256]; char *modname; char *dot; v = PyDict_GetItemString(moddict, "__name__"); if (v == NULL) modname = NULL; else modname = PyString_AsString(v); if (modname == NULL) { PyErr_Clear(); modname = MXNUMBER_MODULE; } /* The symbols from this extension are imported into mx.. We trim the name to not confuse the user with an overly long package path. */ strcpy(fullname, modname); dot = strchr(fullname, '.'); if (dot) dot = strchr(dot+1, '.'); if (dot) strcpy(dot+1, name); else sprintf(fullname, "%s.%s", modname, name); v = PyErr_NewException(fullname, base, NULL); if (v == NULL) return NULL; if (PyDict_SetItemString(moddict,name,v)) return NULL; return v; } /* Helper for adding integer constants to a dictionary. Check for errors with PyErr_Occurred() */ static void insint(PyObject *dict, char *name, int value) { PyObject *v = PyInt_FromLong((long)value); PyDict_SetItemString(dict, name, v); Py_XDECREF(v); } #if 0 /* Helper for adding string constants to a dictionary. Check for errors with PyErr_Occurred() */ static void insstr(PyObject *dict, char *name, char *value) { PyObject *v = PyString_FromString(value); PyDict_SetItemString(dict, name, v); Py_XDECREF(v); } #endif /* Helper for adding objects to dictionaries. Check for errors with PyErr_Occurred() */ static void insobj(PyObject *dict, char *name, PyObject *v) { PyDict_SetItemString(dict, name, v); Py_XDECREF(v); } static PyObject *notimplemented1(PyObject *v) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } static PyObject *notimplemented2(PyObject *v, PyObject *w) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } static PyObject *notimplemented3(PyObject *u, PyObject *v, PyObject *w) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } /* Find a number and set numberstart, numberend to the slice holding the number (including the sign if available). The number may have leading and trailing whitespace around it which is ignored. *position is updated to the position right after the number (including trailing whitespace). Returns 1 in case of success, 0 in case of failure. */ static int find_integer(char **position, char **numberstart, char **numberend) { char *p = *position; /* Skip leading space */ while (*p != 0 && isspace(*p)) p++; if (*p == 0 || (!isalnum(*p) && *p != '-' && *p != '+')) return 0; *numberstart = p; p++; /* Scan alpha-numerical characters */ while (*p != 0 && isalnum(*p)) p++; *numberend = p; /* Skip trailing spaces */ while (*p != 0 && isspace(*p)) p++; /* Update pointer */ *position = p; return 1; } /* === mxInteger Object ================================================ */ static PyObject *mxInteger_New(void) { mxIntegerObject *obj; #ifdef MXNUMBER_FREELIST if (mxInteger_FreeList) { obj = mxInteger_FreeList; mxInteger_FreeList = *(mxIntegerObject **)mxInteger_FreeList; obj->ob_type = &mxInteger_Type; _Py_NewReference(obj); } else #endif { obj = PyObject_NEW(mxIntegerObject,&mxInteger_Type); if (obj == NULL) return NULL; } /* Init vars */ mpz_init(obj->value); obj->hash = -1; return (PyObject *)obj; } static void mxInteger_Free(mxIntegerObject *obj) { if (obj == NULL) return; /* Free vars */ mpz_clear(obj->value); #ifdef MXNUMBER_FREELIST /* Append to free list */ *(mxIntegerObject **)obj = mxInteger_FreeList; mxInteger_FreeList = obj; #else PyObject_Del(obj); #endif } /* --- Internal functions ---------------------------------------------- */ /* --- API functions --------------------------------------------------- */ static PyObject *mxInteger_FromMPZ(mpz_t value) { mxIntegerObject *obj; obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; mpz_set(obj->value, value); return (PyObject *)obj; } static PyObject *mxInteger_FromLong(long value) { mxIntegerObject *obj; obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; mpz_set_si(obj->value, value); return (PyObject *)obj; } static PyObject *mxInteger_FromDouble(double value) { mxIntegerObject *obj; obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; mpz_set_d(obj->value, value); return (PyObject *)obj; } static PyObject *_mxInteger_FromInteger(mxIntegerObject *other) { mxIntegerObject *obj; obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; mpz_set(obj->value, other->value); return (PyObject *)obj; } static PyObject *_mxInteger_FromFloat(mxFloatObject *other) { mxIntegerObject *obj; obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; mpz_set_f(obj->value, other->value); return (PyObject *)obj; } static PyObject *_mxInteger_FromRational(mxRationalObject *other) { mxIntegerObject *obj; obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; mpz_set_q(obj->value, other->value); return (PyObject *)obj; } static PyObject *mxInteger_FromString(char *value, int base) { mxIntegerObject *obj; char *start, *end; if (value == NULL) { PyErr_BadInternalCall(); return NULL; } obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; /* Check that we have a usable number in the string... */ if (!find_integer(&value, &start, &end)) goto badFormat; if (*value != '\0') goto badFormat; *end = '\0'; if (mpz_set_str(obj->value, start, base)) goto badFormat; return (PyObject *)obj; badFormat: Py_Error(mxNumber_Error, "could not convert string to Integer"); onError: mxInteger_Free(obj); return NULL; } static PyObject *mxInteger_FromPyLong(PyObject *value) { mxIntegerObject *obj = NULL; PyObject *v = NULL; if (value == NULL || !PyLong_Check(value)) { PyErr_BadInternalCall(); return NULL; } obj = (mxIntegerObject *)mxInteger_New(); if (obj == NULL) return NULL; v = PyObject_Str(value); if (v == NULL) goto onError; Py_Assert(PyString_Check(v), PyExc_TypeError, "__str__ must return a string object"); if (mpz_set_str(obj->value, PyString_AS_STRING(v), 0)) Py_Error(mxNumber_Error, "could not convert long to Integer"); return (PyObject *)obj; onError: if (obj) mxInteger_Free(obj); Py_XDECREF(v); return NULL; } statichere PyObject *mxInteger_FromObject(PyObject *value) { PyObject *v; if (value == NULL) { PyErr_BadInternalCall(); return NULL; } else if (_mxInteger_Check(value)) { Py_INCREF(value); return value; } else if (PyInt_Check(value)) return mxInteger_FromLong(PyInt_AS_LONG(value)); else if (PyString_Check(value)) /* Determine the base by looking at the string prefix */ return mxInteger_FromString(PyString_AS_STRING(value), 0); else if (PyFloat_Check(value)) /* This will lose precision... */ return mxInteger_FromDouble(PyFloat_AS_DOUBLE(value)); #if 0 else if (_mxFloat_Check(value)) return _mxInteger_FromFloat((mxFloatObject *)value); else if (_mxRational_Check(value)) return _mxInteger_FromRational((mxRationalObject *)value); #endif else if (PyLong_Check(value)) return mxInteger_FromPyLong(value); /* Try to convert via __long__ method */ v = PyNumber_Long(value); if (v == NULL) Py_Error(PyExc_TypeError, "can't convert object to mx.Number.Integer"); return mxInteger_FromPyLong(v); onError: return NULL; } static long mxInteger_AsLong(PyObject *obj) { if (obj == NULL || !_mxInteger_Check(obj)) { PyErr_BadInternalCall(); return -1; } /* Overflow checks */ if ((mpz_cmp(((mxIntegerObject *)obj)->value, max_slong) > 0) || (mpz_cmp(((mxIntegerObject *)obj)->value, min_slong) < 0)) Py_Error(PyExc_OverflowError, "Integer cannot be converted to a Python integer"); return mpz_get_si(((mxIntegerObject *)obj)->value); onError: return -1; } static double mxInteger_AsDouble(PyObject *obj) { if (obj == NULL || !_mxInteger_Check(obj)) { PyErr_BadInternalCall(); return -1.0; } return mpz_get_d(((mxIntegerObject *)obj)->value); } static PyObject *mxInteger_AsStringObject(PyObject *obj, int base) { char *buffer = NULL; PyObject *v; if (obj == NULL || !_mxInteger_Check(obj)) { PyErr_BadInternalCall(); return NULL; } buffer = mpz_get_str(NULL, base, ((mxIntegerObject *)obj)->value); if (buffer == NULL) Py_Error(mxNumber_Error, "conversion to string failed"); v = PyString_FromString(buffer); free(buffer); return v; onError: if (buffer) free(buffer); return NULL; } static PyObject *mxInteger_AsPyLong(PyObject *obj) { char *buffer = NULL; PyObject *v; if (obj == NULL || !_mxInteger_Check(obj)) { PyErr_BadInternalCall(); return NULL; } buffer = mpz_get_str(NULL, 36, ((mxIntegerObject *)obj)->value); if (buffer == NULL) Py_Error(mxNumber_Error, "conversion to string failed"); v = PyLong_FromString(buffer, NULL, 36); free(buffer); return v; onError: if (buffer) free(buffer); return NULL; } /* --- Slots ----------------------------------------------------------- */ static PyObject *mxInteger_AsPyFloat(PyObject *obj) { double value = mxInteger_AsDouble(obj); return PyFloat_FromDouble(value); } static PyObject *mxInteger_AsPyInt(PyObject *obj) { long value = mxInteger_AsLong(obj); if (value == -1 && PyErr_Occurred()) return NULL; return PyInt_FromLong(value); } static int mxInteger_NonZero(PyObject *obj) { int i = mpz_sgn(((mxIntegerObject *)obj)->value); return i != 0; } static PyObject *mxInteger_Str(PyObject *obj) { return mxInteger_AsStringObject(obj, 10); } static PyObject *mxInteger_Repr(PyObject *obj) { return mxInteger_AsStringObject(obj, 10); } static PyObject *mxInteger_Getattr(PyObject *obj, char *name) { #if 0 /* XXX Add some real attributes... */ if (Py_WantAttr(name,"one")) return PyInt_FromLong(1); else if (Py_WantAttr(name,"__members__")) return Py_BuildValue("[s]", "one" ); #endif return Py_FindMethod(mxInteger_Methods, obj, name); onError: return NULL; } static int mxInteger_Coerce(PyObject **left, PyObject **right) { if (left == right) { Py_INCREF(*left); Py_INCREF(*right); return 0; } /* Coerce to floats before comparing in case floats are involved */ if ((PyFloat_Check(*left) || PyFloat_Check(*right))) { *left = mxNumber_AsPyFloat(*left); if (*left == NULL) goto onError; *right = mxNumber_AsPyFloat(*right); if (*right == NULL) { Py_DECREF(*left); goto onError; } return 0; } /* Coerce to Integers */ *left = mxInteger_FromObject(*left); if (*left == NULL) goto onError; *right = mxInteger_FromObject(*right); if (*right == NULL) { Py_DECREF(*left); goto onError; } return 0; onError: /* XXX Should perhaps clear the exception and return 1 instead ?! */ return -1; } static int mxInteger_Compare(PyObject *left, PyObject *right) { int rc; if (left == right) return 0; /* Short-cut */ if (_mxInteger_Check(left) && _mxInteger_Check(right)) return mpz_cmp(((mxIntegerObject *)left)->value, ((mxIntegerObject *)right)->value); /* Coerce to floats before comparing in case floats are involved */ if ((PyFloat_Check(left) || PyFloat_Check(right))) { left = mxNumber_AsPyFloat(left); if (left == NULL) goto onError; right = mxNumber_AsPyFloat(right); if (right == NULL) { Py_DECREF(left); goto onError; } rc = PyObject_Compare(left, right); Py_DECREF(left); Py_DECREF(right); return rc; } /* Coerce to Integers */ left = mxInteger_FromObject(left); if (left == NULL) goto onError; right = mxInteger_FromObject(right); if (right == NULL) { Py_DECREF(left); goto onError; } rc = mpz_cmp(((mxIntegerObject *)left)->value, ((mxIntegerObject *)right)->value); Py_DECREF(left); Py_DECREF(right); return rc; onError: return -1; } static long mxInteger_Hash(mxIntegerObject *left) { long hash = left->hash; PyObject *v; if (hash != -1) return hash; /* Use the Python long hash value as basis since this does all the tricks needed to assure that a==b => hash values are equal too (at least in most cases); XXX This is very expensive !!! */ v = mxInteger_AsPyLong((PyObject *)left); if (v == NULL) return -1; hash = PyObject_Hash(v); Py_DECREF(v); left->hash = hash; return hash; } #define mxInteger_1x1_Operation(fctname, pyop, apiname) \ static \ PyObject *fctname(PyObject *left) \ { \ PyObject *result; \ \ left = mxInteger_FromObject(left); \ if (left == NULL) \ goto onError; \ \ result = mxInteger_New(); \ if (result == NULL) \ goto onError; \ \ apiname(((mxIntegerObject *)result)->value, \ ((mxIntegerObject *)left)->value); \ \ Py_DECREF(left); \ return result; \ \ onError: \ Py_XDECREF(left); \ return NULL; \ } #define mxInteger_2x1_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right) \ { \ PyObject *result; \ \ if (redir) { \ if (_mxFloat_Check(left) || _mxFloat_Check(right)) \ return mxNumber_BinaryFloatOperation(pyop, left, right); \ if (PyFloat_Check(left) || PyFloat_Check(right)) \ return mxNumber_BinaryPyFloatOperation(pyop, left, right); \ if (_mxRational_Check(left) || _mxRational_Check(right)) \ return mxNumber_BinaryRationalOperation(pyop, left, right); \ } \ \ left = mxInteger_FromObject(left); \ if (left == NULL) \ return NULL; \ \ right = mxInteger_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return NULL; \ } \ \ result = mxInteger_New(); \ if (result == NULL) \ goto onError; \ \ apiname(((mxIntegerObject *)result)->value, \ ((mxIntegerObject *)left)->value, \ ((mxIntegerObject *)right)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ return result; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ return result; \ } #define mxInteger_2x2_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right) \ { \ PyObject *result1, *result2; \ PyObject *v; \ \ if (redir) { \ if (_mxFloat_Check(left) || _mxFloat_Check(right)) \ return mxNumber_BinaryFloatOperation(pyop, left, right); \ if (PyFloat_Check(left) || PyFloat_Check(right)) \ return mxNumber_BinaryPyFloatOperation(pyop, left, right); \ if (_mxRational_Check(left) || _mxRational_Check(right)) \ return mxNumber_BinaryRationalOperation(pyop, left, right); \ } \ \ left = mxInteger_FromObject(left); \ if (left == NULL) \ return left; \ \ right = mxInteger_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return right; \ } \ \ result1 = mxInteger_New(); \ if (result1 == NULL) \ goto onError; \ \ result2 = mxInteger_New(); \ if (result2 == NULL) { \ Py_DECREF(result1); \ goto onError; \ } \ \ v = PyTuple_New(2); \ if (v == NULL) { \ Py_DECREF(result1); \ Py_DECREF(result2); \ goto onError; \ } \ \ PyTuple_SET_ITEM(v, 0, result1); \ PyTuple_SET_ITEM(v, 1, result2); \ \ apiname(((mxIntegerObject *)result1)->value, \ ((mxIntegerObject *)result2)->value, \ ((mxIntegerObject *)left)->value, \ ((mxIntegerObject *)right)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ return v; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ return NULL; \ } #define mxInteger_3x1_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right, \ PyObject *extra) \ { \ PyObject *result; \ \ if (redir && \ (PyFloat_Check(left) || PyFloat_Check(right)) || \ PyFloat_Check(extra)) \ return mxNumber_TernaryPyFloatOperation(pyop, left, right, \ extra); \ \ left = mxInteger_FromObject(left); \ if (left == NULL) \ return NULL; \ \ right = mxInteger_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return NULL; \ } \ \ extra = mxInteger_FromObject(extra); \ if (extra == NULL) { \ Py_DECREF(left); \ Py_DECREF(right); \ return NULL; \ } \ \ result = mxInteger_New(); \ if (result == NULL) \ goto onError; \ \ apiname(((mxIntegerObject *)result)->value, \ ((mxIntegerObject *)left)->value, \ ((mxIntegerObject *)right)->value, \ ((mxIntegerObject *)extra)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ Py_DECREF(extra); \ return result; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ Py_DECREF(extra); \ return result; \ } /* Number Slots */ mxInteger_2x1_Operation(mxInteger_Add, PyNumber_Add, mpz_add, 1); mxInteger_2x1_Operation(mxInteger_Subtract, PyNumber_Subtract, mpz_sub, 1); mxInteger_2x1_Operation(mxInteger_Multiply, PyNumber_Multiply, mpz_mul, 1); mxInteger_2x1_Operation(mxInteger_Divide, PyNumber_Divide, mpz_tdiv_q, 1); mxInteger_1x1_Operation(mxInteger_Negative, PyNumber_Negative, mpz_neg); mxInteger_1x1_Operation(mxInteger_Absolute, PyNumber_Absolute, mpz_abs); mxInteger_1x1_Operation(mxInteger_Invert, PyNumber_Invert, mpz_com); mxInteger_2x1_Operation(mxInteger_And, PyNumber_And, mpz_and, 0); mxInteger_2x1_Operation(mxInteger_Or, PyNumber_Or, mpz_ior, 0); mxInteger_2x1_Operation(mxInteger_Remainder, PyNumber_Remainder, mpz_tdiv_r, 1); mxInteger_2x2_Operation(mxInteger_Divmod, PyNumber_Divmod, mpz_tdiv_qr, 1); static PyObject *mxInteger_Xor(PyObject *left, PyObject *right) { PyObject *result; mpz_t temp; left = mxInteger_FromObject(left); if (left == NULL) return left; right = mxInteger_FromObject(right); if (right == NULL) { Py_DECREF(left); return right; } result = mxInteger_New(); if (result == NULL) return NULL; /* XOR = (left OR right) AND COM (left AND right) -- SLOW !!! */ mpz_init(temp); mpz_ior(((mxIntegerObject *)result)->value, ((mxIntegerObject *)left)->value, ((mxIntegerObject *)right)->value); mpz_and(temp, ((mxIntegerObject *)left)->value, ((mxIntegerObject *)right)->value); mpz_com(temp, temp); mpz_and(((mxIntegerObject *)result)->value, temp, temp); mpz_clear(temp); Py_DECREF(left); Py_DECREF(right); return result; } static PyObject *mxInteger_Power(PyObject *base, PyObject *exp, PyObject *mod) { PyObject *result = NULL; if ((PyFloat_Check(base) || PyFloat_Check(exp)) || PyFloat_Check(mod)) return mxNumber_TernaryPyFloatOperation(PyNumber_Power, base, exp, mod); base = mxInteger_FromObject(base); if (base == NULL) return NULL; exp = mxInteger_FromObject(exp); if (exp == NULL) { Py_DECREF(base); return NULL; } if (mpz_sgn(((mxIntegerObject *)exp)->value) < 0) Py_Error(PyExc_ValueError, "can't raise to a negative power"); result = mxInteger_New(); if (result == NULL) goto onError; if (mod == Py_None) { unsigned long uiexp; if (!mpz_fits_ulong_p(((mxIntegerObject *)exp)->value)) Py_Error(PyExc_ValueError, "exponent too large"); uiexp = mpz_get_ui(((mxIntegerObject *)exp)->value); mpz_pow_ui(((mxIntegerObject *)result)->value, ((mxIntegerObject *)base)->value, uiexp); } else { mod = mxInteger_FromObject(mod); if (mod == NULL) goto onError; mpz_powm(((mxIntegerObject *)result)->value, ((mxIntegerObject *)base)->value, ((mxIntegerObject *)exp)->value, ((mxIntegerObject *)mod)->value); Py_DECREF(mod); } Py_DECREF(base); Py_DECREF(exp); return result; onError: Py_DECREF(base); Py_DECREF(exp); Py_XDECREF(result); return NULL; } static PyObject *mxInteger_notimplemented1(PyObject *v) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } static PyObject *mxInteger_notimplemented2(PyObject *v, PyObject *w) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } static PyObject *mxInteger_notimplemented3(PyObject *v, PyObject *w, PyObject *u) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } /* --- Methods --------------------------------------------------------- */ #define obj ((mxIntegerObject*)self) Py_C_Function( mxInteger_sign, "sign()\n" "Return the sign of the number.") { Py_NoArgsCheck(); return PyInt_FromLong((long)mpz_sgn(obj->value)); onError: return NULL; } #ifdef COPY_PROTOCOL Py_C_Function( mxInteger_copy, "copy([memo])\n\n" "Return a new reference for the instance. This function\n" "is used for the copy-protocol. Real copying doesn't take\n" "place, since the instances are immutable.") { PyObject *memo; Py_GetArg("|O",memo); Py_INCREF(obj); return (PyObject *)obj; onError: return NULL; } #endif Py_C_Function( mxInteger_sqrt, "sqrt()\n" "Return the square root of the number.") { PyObject *result; Py_NoArgsCheck(); result = mxInteger_New(); if (result == NULL) return NULL; mpz_sqrt(((mxIntegerObject *)result)->value, obj->value); return result; onError: return NULL; } Py_C_Function( mxInteger_root, "root(n)\n" "Return the (truncated) n-th root of the number.") { PyObject *result; unsigned long n; Py_GetArg("l", n); Py_Assert(n > 0, PyExc_ValueError, "root must be positive"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_root(((mxIntegerObject *)result)->value, obj->value, n); return result; onError: return NULL; } Py_C_Function( mxInteger_has_root, "has_root(n)\n" "Return 1/0 iff number has an (exact) n-th root.") { int rc; unsigned long n; mpz_t temp; Py_GetArg("l", n); Py_Assert(n > 0, PyExc_ValueError, "root must be positive"); mpz_init(temp); rc = mpz_root(temp, obj->value, n); mpz_clear(temp); return PyInt_FromLong((rc != 0)); onError: return NULL; } Py_C_Function( mxInteger_is_perfect_power, "is_perfect_power()\n" "True iff number is a perfect power.") { Py_NoArgsCheck(); return PyInt_FromLong((long)(mpz_perfect_power_p(obj->value) != 0)); onError: return NULL; } Py_C_Function( mxInteger_is_perfect_square, "is_perfect_square()\n" "True iff number is a perfect square.") { Py_NoArgsCheck(); return PyInt_FromLong((long)(mpz_perfect_square_p(obj->value) != 0)); onError: return NULL; } Py_C_Function( mxInteger_prime, "prime([reps=10])\n" "Return 1 if number is a prime, 2 if number is probably\n" "prime and 3 if number surely prime according to the\n" "Miller-Rabin test. 0 is returned for definite non-primes.\n" "Higher values for reps reduce the probablitiy for failure\n" "of the test.") { int rc; int reps = 10; Py_GetArg("|i", reps); Py_Assert(reps > 0, PyExc_ValueError, "reps must be positive"); rc = mpz_probab_prime_p(obj->value, reps); return PyInt_FromLong((rc != 0)); onError: return NULL; } Py_C_Function( mxInteger_gcd, "gcd(other)\n" "Return the (positive) GCD of number and other.") { PyObject *other, *result; Py_GetArg("O", other); other = mxInteger_FromObject(other); if (other == NULL) return other; result = mxInteger_New(); if (result == NULL) return NULL; mpz_gcd(((mxIntegerObject *)result)->value, obj->value, ((mxIntegerObject *)other)->value); Py_DECREF(other); return result; onError: return NULL; } Py_C_Function( mxInteger_lcm, "lcm(other)\n" "Return the (positive) LCM of number and other.") { PyObject *other, *result; Py_GetArg("O", other); other = mxInteger_FromObject(other); if (other == NULL) return other; result = mxInteger_New(); if (result == NULL) return NULL; mpz_lcm(((mxIntegerObject *)result)->value, obj->value, ((mxIntegerObject *)other)->value); Py_DECREF(other); return result; onError: return NULL; } Py_C_Function( mxInteger_jacobi, "jacobi()\n" "Return the Jacobi symbol for number.") { PyObject *result; Py_NoArgsCheck(); if (mpz_sgn(obj->value) <= 0) Py_Error(PyExc_ValueError, "number must be positive"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_jacobi(((mxIntegerObject *)result)->value, obj->value); return result; onError: return NULL; } Py_C_Function( mxInteger_legendre, "legendre()\n" "Return the Legendre symbol for number.") { PyObject *result; Py_NoArgsCheck(); if (mpz_sgn(obj->value) <= 0) Py_Error(PyExc_ValueError, "number must be positive"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_legendre(((mxIntegerObject *)result)->value, obj->value); return result; onError: return NULL; } Py_C_Function( mxInteger_factorial, "factorial()\n" "Return the factorial of the number.") { PyObject *result; unsigned long n; Py_NoArgsCheck(); if (mpz_sgn(obj->value) <= 0) Py_Error(PyExc_ValueError, "number must be positive"); if (!mpz_fits_ulong_p(obj->value)) Py_Error(PyExc_ValueError, "number too big to calculate factorial"); result = mxInteger_New(); if (result == NULL) return NULL; n = mpz_get_ui(obj->value); mpz_fac_ui(((mxIntegerObject *)result)->value, n); return result; onError: return NULL; } Py_C_Function( mxInteger_over, "over(k)\n" "Return the binomial coefficient number over k.") { PyObject *result; unsigned long k; Py_GetArg("l", k); Py_Assert(k >= 0, PyExc_ValueError, "argument must be non-negative"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_bin_ui(((mxIntegerObject *)result)->value, obj->value, k); return result; onError: return NULL; } Py_C_Function( mxInteger_popcount, "popcount()\n" "Return the population count for number.\n" "Number must be positive.") { unsigned long n; Py_NoArgsCheck(); if (mpz_sgn(obj->value) <= 0) Py_Error(PyExc_ValueError, "number must be positive"); n = mpz_popcount(obj->value); return PyInt_FromLong(n); onError: return NULL; } Py_C_Function( mxInteger_hamdist, "hamdist(other)\n" "Return the Hamming Distance between number and other.\n" "Both values must be positive.") { PyObject *other = NULL; unsigned long n; Py_GetArg("O", other); other = mxInteger_FromObject(other); if (other == NULL) return other; if (mpz_sgn(obj->value) <= 0) Py_Error(PyExc_ValueError, "number must be positive"); if (mpz_sgn(((mxIntegerObject *)other)->value) <= 0) Py_Error(PyExc_ValueError, "argument must be positive"); n = mpz_hamdist(obj->value, ((mxIntegerObject *)other)->value); Py_DECREF(other); return PyInt_FromLong(n); onError: Py_XDECREF(other); return NULL; } /* XXX Add a new type Bitfield which is mutable and supports mpz_setbit, mpz_clrbit, mpz_tstbit */ Py_C_Function( mxInteger_odd, "odd()\n" "True iff number is odd.") { Py_NoArgsCheck(); return PyInt_FromLong((long)(mpz_odd_p(obj->value) != 0)); onError: return NULL; } Py_C_Function( mxInteger_even, "even()\n" "True iff number is even.") { Py_NoArgsCheck(); return PyInt_FromLong((long)(mpz_even_p(obj->value) != 0)); onError: return NULL; } #undef obj /* --- Python Type Tables ---------------------------------------------- */ static PyNumberMethods mxInteger_TypeAsNumber = { /* These slots are not NULL-checked, so we must provide dummy functions */ (binaryfunc)mxInteger_Add, /*nb_add*/ (binaryfunc)mxInteger_Subtract, /*nb_subtract*/ (binaryfunc)mxInteger_Multiply, /*nb_multiply*/ (binaryfunc)mxInteger_Divide, /*nb_divide*/ (binaryfunc)mxInteger_Remainder, /*nb_remainder*/ (binaryfunc)mxInteger_Divmod, /*nb_divmod*/ (ternaryfunc)mxInteger_Power, /*nb_power*/ (unaryfunc)mxInteger_Negative, /*nb_negative*/ (unaryfunc)notimplemented1, /*nb_positive*/ /* Everything below this line EXCEPT nb_nonzero (!) is NULL checked */ (unaryfunc)mxInteger_Absolute, /*nb_absolute*/ (inquiry)mxInteger_NonZero, /*nb_nonzero*/ (unaryfunc)mxInteger_Invert, /*nb_invert*/ (binaryfunc)notimplemented2, /*nb_lshift*/ (binaryfunc)notimplemented2, /*nb_rshift*/ (binaryfunc)mxInteger_And, /*nb_and*/ (binaryfunc)mxInteger_Xor, /*nb_xor*/ (binaryfunc)mxInteger_Or, /*nb_or*/ (coercion)mxInteger_Coerce, /*nb_coerce*/ (unaryfunc)mxInteger_AsPyInt, /*nb_int*/ (unaryfunc)mxInteger_AsPyLong, /*nb_long*/ (unaryfunc)mxInteger_AsPyFloat, /*nb_float*/ (unaryfunc)0, /*nb_oct*/ (unaryfunc)0, /*nb_hex*/ }; statichere PyTypeObject mxInteger_Type = { PyObject_HEAD_INIT(0) /* init at startup ! */ 0, /*ob_size*/ "Integer", /*tp_name*/ sizeof(mxIntegerObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* slots */ (destructor)mxInteger_Free, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ (getattrfunc)mxInteger_Getattr, /*tp_getattr*/ (setattrfunc)0, /*tp_setattr*/ (cmpfunc)mxInteger_Compare, /*tp_compare*/ (reprfunc)mxInteger_Repr, /*tp_repr*/ &mxInteger_TypeAsNumber, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)mxInteger_Hash, /*tp_hash*/ (ternaryfunc)0, /*tp_call*/ (reprfunc)mxInteger_Str, /*tp_str*/ (getattrofunc)0, /*tp_getattro*/ (setattrofunc)0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_CHECKTYPES, /*tp_flags*/ (char*) 0 /*tp_doc*/ }; /* Python Method Table */ statichere PyMethodDef mxInteger_Methods[] = { Py_MethodListEntryNoArgs("sign",mxInteger_sign), #ifdef COPY_PROTOCOL Py_MethodListEntry("copy",mxInteger_copy), #endif Py_MethodListEntryNoArgs("sqrt",mxInteger_sqrt), Py_MethodListEntry("root",mxInteger_root), Py_MethodListEntry("has_root",mxInteger_has_root), Py_MethodListEntryNoArgs("is_perfect_power",mxInteger_is_perfect_power), Py_MethodListEntryNoArgs("is_perfect_square",mxInteger_is_perfect_square), Py_MethodListEntry("prime",mxInteger_prime), Py_MethodListEntry("gcd",mxInteger_gcd), Py_MethodListEntry("lcm",mxInteger_lcm), Py_MethodListEntryNoArgs("jacobi",mxInteger_jacobi), Py_MethodListEntryNoArgs("legendre",mxInteger_legendre), Py_MethodListEntryNoArgs("factorial",mxInteger_factorial), Py_MethodListEntry("over",mxInteger_over), Py_MethodListEntryNoArgs("popcount",mxInteger_popcount), Py_MethodListEntry("hamdist",mxInteger_hamdist), Py_MethodListEntryNoArgs("odd",mxInteger_odd), Py_MethodListEntryNoArgs("even",mxInteger_even), {NULL,NULL} /* end of list */ }; /* === mxRational Object ================================================ */ static PyObject *mxRational_New(void) { mxRationalObject *obj; #ifdef MXNUMBER_FREELIST if (mxRational_FreeList) { obj = mxRational_FreeList; mxRational_FreeList = *(mxRationalObject **)mxRational_FreeList; obj->ob_type = &mxRational_Type; _Py_NewReference(obj); } else #endif { obj = PyObject_NEW(mxRationalObject,&mxRational_Type); if (obj == NULL) return NULL; } /* Init vars */ mpq_init(obj->value); obj->hash = -1; return (PyObject *)obj; } static void mxRational_Free(mxRationalObject *obj) { if (obj == NULL) return; /* Free vars */ mpq_clear(obj->value); #ifdef MXNUMBER_FREELIST /* Append to free list */ *(mxRationalObject **)obj = mxRational_FreeList; mxRational_FreeList = obj; #else PyObject_Del(obj); #endif } /* --- Internal functions ---------------------------------------------- */ /* --- API functions --------------------------------------------------- */ static PyObject *mxRational_FromMPQ(mpq_t value) { mxRationalObject *obj; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set(obj->value, value); return (PyObject *)obj; } static PyObject *mxRational_FromMPZ(mpz_t value) { mxRationalObject *obj; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set_z(obj->value, value); return (PyObject *)obj; } static PyObject *mxRational_FromTwoMPZs(mpz_t numerator, mpz_t denominator) { mxRationalObject *obj; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set_num(obj->value, numerator); mpq_set_den(obj->value, denominator); mpq_canonicalize(obj->value); return (PyObject *)obj; } /* mxRational_FromTwoLongs(): denom must be positive ! */ static PyObject *mxRational_FromTwoLongs(long numerator, long denominator) { mxRationalObject *obj; if (denominator <= 0) Py_Error(PyExc_ValueError, "denominator must be positive"); obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set_si(obj->value, numerator, (unsigned long)denominator); mpq_canonicalize(obj->value); return (PyObject *)obj; onError: return NULL; } static PyObject *mxRational_FromLong(long value) { mxRationalObject *obj; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set_si(obj->value, value, 1); return (PyObject *)obj; onError: return NULL; } static PyObject *mxRational_FromDouble(double value) { mxRationalObject *obj; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set_d(obj->value, value); return (PyObject *)obj; } static PyObject *_mxRational_FromRational(mxRationalObject *other) { mxRationalObject *obj; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; mpq_set(obj->value, other->value); return (PyObject *)obj; } /* Farey Function This is a GNU MP implementation of the following function which Scott David Daniels posted to the Python Cookbook; http://www.activestate.com/ASPN/Python/Cookbook/Recipe/52317 : def farey(v, lim): '''Named after James Farey, an English surveyor. No error checking on args -- lim = max denominator, results are (numerator, denominator), (1,0) is infinity ''' if v < 0: n,d = farey(-v, lim) return -n,d z = lim-lim # get 0 of right type for denominator lower, upper = (z,z+1), (z+1,z) while 1: mediant = (lower[0] + upper[0]), (lower[1]+upper[1]) if v * mediant[1] > mediant[0]: if lim < mediant[1]: return upper lower = mediant elif v * mediant[1] == mediant[0]: if lim >= mediant[1]: return mediant if lower[1] < upper[1]: return lower return upper else: if lim < mediant[1]: return lower upper = mediant A nice proof of the algorithm can be found at "Cut the Knot": http://www.cut-the-knot.com/blue/Farey.html */ #define mx_mpf_copy(a, b) {mpf_init2(a, mpf_get_prec(b)); mpf_set(a, b);} static int farey_rational(mpq_t result, mpf_t value, mpz_t maxdenominator) { mpq_t lower, upper, mediant; mpf_t tmp1, tmp2; int i; /* Handle negative case */ if (mpf_sgn(value) < 0) { mpf_init2(tmp1, mpf_get_prec(value)); mpf_neg(tmp1, value); farey_rational(result, tmp1, maxdenominator); mpq_neg(result, result); mpf_clear(tmp1); return 0; } #ifdef MAL_DEBUG if (DEBUGGING) { printf("farey(): "); mpf_out_str(stdout, 10, 0, value); printf("\n"); } #endif /* value is positive */ mpq_init(lower); mpq_init(upper); mpq_init(mediant); mpf_init(tmp1); mpf_init(tmp2); /* Init lower and upper to (0,1), (1,0) (= +Inf) resp. */ mpq_set_si(lower, 0, 1); mpq_set_si(upper, 1, 0); /* Find closest approx. using interval search */ for (i = 0; i < 100000; i++) { int rc; /* Calc mediant of lower, upper */ mpq_set_num(mediant, mpq_numref(lower)); mpz_add(mpq_numref(mediant), mpq_numref(mediant), mpq_numref(upper)); mpq_set_den(mediant, mpq_denref(lower)); mpz_add(mpq_denref(mediant), mpq_denref(mediant), mpq_denref(upper)); #ifdef MAL_DEBUG if (DEBUGGING) { printf("Iteration %3i: ", i); mpq_out_str(stdout, 10, mediant); printf("\n"); } #endif /* tmp1 = value * den(mediant) */ mpf_set(tmp1, value); mpf_set_z(tmp2, mpq_denref(mediant)); mpf_mul(tmp1, tmp1, tmp2); /* tmp2 = num(mediant) */ mpf_set_z(tmp2, mpq_numref(mediant)); /* compare tmp1 and tmp2 */ rc = mpf_cmp(tmp1, tmp2); if (rc > 0) { if (mpz_cmp(mpq_denref(mediant), maxdenominator) > 0) { mpq_set(result, upper); goto finished; } mpq_set(lower, mediant); } else if (rc == 0) { if (mpz_cmp(mpq_denref(mediant), maxdenominator) <= 0) { mpq_set(result, mediant); goto finished; } if (mpz_cmp(mpq_denref(lower), mpq_denref(upper)) < 0) mpq_set(result, lower); else mpq_set(result, upper); goto finished; } else { if (mpz_cmp(mpq_denref(mediant), maxdenominator) > 0) { mpq_set(result, lower); goto finished; } mpq_set(upper, mediant); } } finished: mpq_clear(lower); mpq_clear(upper); mpq_clear(mediant); mpf_clear(tmp1); mpf_clear(tmp2); /* We have to return a canonicalized rational ! */ mpq_canonicalize(result); #ifdef MAL_DEBUG if (DEBUGGING) { printf("Result: "); mpq_out_str(stdout, 10, result); printf("\n"); } #endif return 0; } static PyObject *mxRational_FromFareyApprox(PyObject *value, PyObject *maxdenominator) { mxRationalObject *obj; mpq_t approx; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; value = mxFloat_FromObject(value); maxdenominator = mxInteger_FromObject(maxdenominator); mpq_init(approx); if (farey_rational(approx, ((mxFloatObject *)value)->value, ((mxIntegerObject *)maxdenominator)->value)) { mpq_clear(approx); Py_Error(mxNumber_Error, "farey() function failed"); } mpq_set(obj->value, approx); mpq_clear(approx); Py_DECREF(value); Py_DECREF(maxdenominator); return (PyObject *)obj; onError: Py_DECREF(obj); Py_DECREF(value); Py_DECREF(maxdenominator); return NULL; } static PyObject *_mxRational_FromFloat(mxFloatObject *other) { mxRationalObject *obj; int precision; mpf_t tmp; obj = (mxRationalObject *)mxRational_New(); if (obj == NULL) return NULL; precision = mpf_get_prec(other->value); /* Numerator ( = trunc(value * 2^precision) )*/ mpf_init(tmp); mpf_set(tmp, other->value); mpf_mul_2exp(tmp, tmp, precision); mpf_trunc(tmp, tmp); mpz_set_f(mpq_numref(obj->value), tmp); mpf_clear(tmp); /* Denominator ( = 2^precision ) */ mpz_set_ui(mpq_denref(obj->value), 1); mpz_mul_2exp(mpq_denref(obj->value), mpq_denref(obj->value), precision); mpq_canonicalize(obj->value); return (PyObject *)obj; onError: Py_DECREF(obj); return NULL; } static PyObject *_mxRational_FromInteger(register mxIntegerObject *other) { return mxRational_FromMPZ(((mxIntegerObject *)other)->value); } static PyObject *_mxRational_FromTwoIntegers(register mxIntegerObject *numerator, register mxIntegerObject *denominator) { return mxRational_FromTwoMPZs(((mxIntegerObject *)numerator)->value, ((mxIntegerObject *)denominator)->value); } /* Parser for rational strings. Possible formats are: A. "[-]123" B. "[-]2/3" C. "[-]44 2/3" */ static PyObject *mxRational_FromString(char *value, int base) { PyObject *result; char *p, *number1, *number2, *number3, *delimiter; if (value == NULL) { PyErr_BadInternalCall(); return NULL; } /* Copy the string */ value = strdup(value); if (value == NULL) { PyErr_NoMemory(); goto onError; } p = value; if (!find_integer(&p, &number1, &delimiter)) goto badFormat; /* Format A */ if (*p == 0) { mpz_t numerator; mpz_init(numerator); DPRINTF("format A\n"); *delimiter = '\0'; if (mpz_set_str(numerator, number1, base)) goto badFormatA; result = mxRational_FromMPZ(numerator); goto finished; badFormatA: mpz_clear(numerator); goto badFormat; } /* Format B */ if (*p == '/') { mpz_t numerator, denominator; mpz_init(numerator); mpz_init(denominator); DPRINTF("format B1\n"); *delimiter = '\0'; p++; if (!find_integer(&p, &number2, &delimiter)) goto badFormatB; if (*p != 0) goto badFormatB; DPRINTF("format B2\n"); *delimiter = '\0'; if (mpz_set_str(numerator, number1, base)) goto badFormatB; if (mpz_set_str(denominator, number2, base)) goto badFormatB; DPRINTF("format B3\n"); result = mxRational_FromTwoMPZs(numerator, denominator); mpz_clear(numerator); mpz_clear(denominator); goto finished; badFormatB: mpz_clear(numerator); mpz_clear(denominator); goto badFormat; } /* Format C */ if (isalnum(*p)) { mpz_t numerator, denominator; mpq_t rational, whole, fraction; mpz_init(numerator); mpz_init(denominator); mpq_init(rational); mpq_init(whole); mpq_init(fraction); DPRINTF("format C1\n"); *delimiter = '\0'; if (!find_integer(&p, &number2, &delimiter)) goto badFormatC; if (*p == 0 || *p != '/') goto badFormatC; DPRINTF("format C2\n"); *delimiter = '\0'; p++; if (!find_integer(&p, &number3, &delimiter)) goto badFormatC; if (*p != 0) goto badFormatC; DPRINTF("format C3\n"); *delimiter = '\0'; if (mpz_set_str(numerator, number1, base)) goto badFormatC; mpq_set_z(whole, numerator); if (mpz_set_str(numerator, number2, base)) goto badFormatC; if (mpz_set_str(denominator, number3, base)) goto badFormatC; DPRINTF("format C4\n"); mpq_set_num(fraction, numerator); mpq_set_den(fraction, denominator); mpq_canonicalize(fraction); if (mpq_sgn(whole) >= 0) mpq_add(rational, whole, fraction); else mpq_sub(rational, whole, fraction); result = mxRational_FromMPQ(rational); mpq_clear(rational); mpq_clear(whole); mpq_clear(fraction); mpz_clear(numerator); mpz_clear(denominator); goto finished; badFormatC: mpq_clear(rational); mpq_clear(whole); mpq_clear(fraction); mpz_clear(numerator); mpz_clear(denominator); /* Fall through... */ } /* Bad format */ goto badFormat; badFormat: Py_Error(mxNumber_Error, "could not parse Rational string format"); onError: result = NULL; finished: free(value); return result; } static PyObject *mxRational_FromPyLong(PyObject *value) { PyObject *v = NULL; PyObject *result; if (value == NULL || !PyLong_Check(value)) { PyErr_BadInternalCall(); return NULL; } v = PyObject_Str(value); if (v == NULL) goto onError; Py_Assert(PyString_Check(v), PyExc_TypeError, "__str__ must return a string object"); result = mxRational_FromString(PyString_AS_STRING(v), 0); Py_DECREF(v); return result; onError: Py_XDECREF(v); return NULL; } statichere PyObject *mxRational_FromObject(PyObject *value) { mxRationalObject *obj; PyObject *v; if (value == NULL) { PyErr_BadInternalCall(); return NULL; } else if (_mxRational_Check(value)) { Py_INCREF(value); return value; } else if (PyInt_Check(value)) return mxRational_FromLong(PyInt_AS_LONG(value)); else if (PyString_Check(value)) /* Determine the base by looking at the string prefixes */ return mxRational_FromString(PyString_AS_STRING(value), 0); else if (PyFloat_Check(value)) /* This will lose precision... */ return mxRational_FromDouble(PyFloat_AS_DOUBLE(value)); else if (_mxInteger_Check(value)) return _mxRational_FromInteger((mxIntegerObject *)value); else if (_mxFloat_Check(value)) return _mxRational_FromFloat((mxFloatObject *)value); else if (PyLong_Check(value)) return mxRational_FromPyLong(value); obj = NULL; /* Try to convert via __long__ method */ v = PyNumber_Long(value); if (v == NULL) Py_Error(PyExc_TypeError, "can't convert object to mx.Number.Rational"); return mxRational_FromPyLong(v); onError: if (obj) mxRational_Free(obj); return NULL; } static PyObject *mxRational_FromTwoIntegers(PyObject *numerator, PyObject *denominator) { mxRationalObject *obj; if (numerator == NULL || denominator == NULL) { PyErr_BadInternalCall(); return NULL; } /* Short-cut */ if (_mxInteger_Check(numerator) && _mxInteger_Check(denominator)) return _mxRational_FromTwoIntegers((mxIntegerObject *)numerator, (mxIntegerObject *)denominator); /* Convert the arguments to Integers */ numerator = mxInteger_FromObject(numerator); if (numerator == NULL) goto onError; denominator = mxInteger_FromObject(denominator); if (denominator == NULL) { Py_DECREF(numerator); goto onError; } obj = (mxRationalObject *)_mxRational_FromTwoIntegers( (mxIntegerObject *)numerator, (mxIntegerObject *)denominator); Py_DECREF(numerator); Py_DECREF(denominator); return (PyObject *)obj; onError: return NULL; } static PyObject *mxRational_FromTwoObjects(PyObject *numerator, PyObject *denominator) { if (numerator == NULL || denominator == NULL) { PyErr_BadInternalCall(); return NULL; } if (PyInt_Check(numerator) && PyInt_Check(denominator)) return mxRational_FromTwoLongs(PyInt_AS_LONG(numerator), PyInt_AS_LONG(denominator)); else if (_mxInteger_Check(numerator) && _mxInteger_Check(denominator)) return _mxRational_FromTwoIntegers((mxIntegerObject *)numerator, (mxIntegerObject *)denominator); #if 0 else if (PyString_Check(value)) /* Determine the base by looking at the string prefixes */ return mxRational_FromString(PyString_AS_STRING(value), 0); #endif #if 0 else if (_mxFloat_Check(value)) return _mxRational_FromFloat((mxFloatObject *)value); else if (_mxRational_Check(value)) return _mxRational_FromRational((mxRationalObject *)value); #endif return mxRational_FromTwoIntegers(numerator, denominator); onError: return NULL; } static PyObject *mxRational_Numerator(register PyObject *obj) { if (obj == NULL || !_mxRational_Check(obj)) { PyErr_BadInternalCall(); return NULL; } return mxInteger_FromMPZ(mpq_numref(((mxRationalObject *)obj)->value)); } static PyObject *mxRational_Denominator(register PyObject *obj) { if (obj == NULL || !_mxRational_Check(obj)) { PyErr_BadInternalCall(); return NULL; } return mxInteger_FromMPZ(mpq_denref(((mxRationalObject *)obj)->value)); } static long mxRational_AsLong(PyObject *obj) { if (obj == NULL || !_mxRational_Check(obj)) { PyErr_BadInternalCall(); return -1; } Py_Error(PyExc_OverflowError, "Rational cannot be converted to a Python integer"); onError: return -1; } static double mxRational_AsDouble(PyObject *obj) { if (obj == NULL || !_mxRational_Check(obj)) { PyErr_BadInternalCall(); return -1.0; } return mpq_get_d(((mxRationalObject *)obj)->value); } /* Convert a Rational to a string in the given base. For base10 representations, if precision is > 0, the Rational is formatted using a decimal representation having that many digits. Setting precision to 0 causes a rational representation to be used. */ static PyObject *mxRational_AsStringObject(PyObject *obj, int base, int precision) { int size; char *p; char *buffer = NULL; PyObject *v; if (obj == NULL || !_mxRational_Check(obj)) { PyErr_BadInternalCall(); return NULL; } if (precision <= 0) { /* String format: "[-]/" */ size = (mpz_sizeinbase(mpq_numref(((mxRationalObject *)obj)->value), base) + mpz_sizeinbase(mpq_denref(((mxRationalObject *)obj)->value), base) + 4); buffer = PyMem_NEW(char, size); if (buffer == NULL) return PyErr_NoMemory(); p = mpz_get_str(buffer, base, mpq_numref(((mxRationalObject *)obj)->value)); if (p == NULL) Py_Error(mxNumber_Error, "conversion to string failed"); p = buffer + strlen(buffer); *p++ = '/'; p = mpz_get_str(p, base, mpq_denref(((mxRationalObject *)obj)->value)); if (p == NULL) Py_Error(mxNumber_Error, "conversion to string failed"); } else if (base == 10) { double value; int len; /* String format: %g with the given precision using the same strategy as is used for Python floats */ value = mpq_get_d(((mxRationalObject *)obj)->value); size = precision + 10; buffer = PyMem_NEW(char, size); if (buffer == NULL) return PyErr_NoMemory(); len = sprintf(buffer, "%.*g", precision, value); if (len <= 0) Py_Error(PyExc_TypeError, "could not stringify Rational"); if (len >= size) Py_Error(PyExc_SystemError, "buffer overrun in str(Rational)"); /* Make sure that the generated string includes a decimal point or other "float" indicator */ for (p = buffer; *p != '\0'; p++) if (*p == '.') break; if (*p == '\0') { *p++ = '.'; *p++ = '0'; *p++ = '\0'; } } else Py_Error(PyExc_ValueError, "Rationals with fixed precision must use base10"); v = PyString_FromString(buffer); free(buffer); return v; onError: if (buffer) free(buffer); return NULL; } static PyObject *mxRational_AsPyLong(PyObject *obj) { char *buffer = NULL; PyObject *v; if (obj == NULL || !_mxRational_Check(obj)) { PyErr_BadInternalCall(); return NULL; } #if 0 buffer = mpq_get_str(NULL, 36, ((mxRationalObject *)obj)->value); #else buffer = NULL; #endif if (buffer == NULL) Py_Error(mxNumber_Error, "conversion to string failed"); v = PyLong_FromString(buffer, NULL, 36); free(buffer); return v; onError: if (buffer) free(buffer); return NULL; } /* --- Slots ----------------------------------------------------------- */ static PyObject *mxRational_AsPyFloat(PyObject *obj) { double value = mxRational_AsDouble(obj); return PyFloat_FromDouble(value); } static PyObject *mxRational_AsPyInt(PyObject *obj) { long value = mxRational_AsLong(obj); if (value == -1 && PyErr_Occurred()) return NULL; return PyInt_FromLong(value); } static int mxRational_NonZero(PyObject *obj) { int i = mpq_sgn(((mxRationalObject *)obj)->value); return i != 0; } static PyObject *mxRational_Str(PyObject *obj) { return mxRational_AsStringObject(obj, 10, 17); } static PyObject *mxRational_Repr(PyObject *obj) { return mxRational_AsStringObject(obj, 10, 0); } static PyObject *mxRational_Getattr(PyObject *obj, char *name) { if (Py_WantAttr(name,"numerator")) return mxRational_Numerator(obj); else if (Py_WantAttr(name,"denominator")) return mxRational_Denominator(obj); else if (Py_WantAttr(name,"__members__")) return Py_BuildValue("[ss]", "numerator", "denominator" ); return Py_FindMethod(mxRational_Methods, obj, name); onError: return NULL; } static int mxRational_Coerce(PyObject **left, PyObject **right) { if (left == right) { Py_INCREF(*left); Py_INCREF(*right); return 0; } /* Coerce to floats before comparing in case floats are involved */ if ((PyFloat_Check(*left) || PyFloat_Check(*right))) { *left = mxNumber_AsPyFloat(*left); if (*left == NULL) goto onError; *right = mxNumber_AsPyFloat(*right); if (*right == NULL) { Py_DECREF(*left); goto onError; } return 0; } /* Coerce to Rationals */ *left = mxRational_FromObject(*left); if (*left == NULL) goto onError; *right = mxRational_FromObject(*right); if (*right == NULL) { Py_DECREF(*left); goto onError; } return 0; onError: /* XXX Should perhaps clear the exception and return 1 instead ?! */ return -1; } static int mxRational_Compare(PyObject *left, PyObject *right) { int rc; if (left == right) return 0; /* Short-cut */ if (_mxRational_Check(left) && _mxRational_Check(right)) return mpq_cmp(((mxRationalObject *)left)->value, ((mxRationalObject *)right)->value); /* Coerce to floats before comparing in case floats are involved */ if ((PyFloat_Check(left) || PyFloat_Check(right))) { left = mxNumber_AsPyFloat(left); if (left == NULL) goto onError; right = mxNumber_AsPyFloat(right); if (right == NULL) { Py_DECREF(left); goto onError; } rc = PyObject_Compare(left, right); Py_DECREF(left); Py_DECREF(right); return rc; } /* Coerce to Rationals */ left = mxRational_FromObject(left); if (left == NULL) goto onError; right = mxRational_FromObject(right); if (right == NULL) { Py_DECREF(left); goto onError; } rc = mpq_cmp(((mxRationalObject *)left)->value, ((mxRationalObject *)right)->value); Py_DECREF(left); Py_DECREF(right); return rc; onError: return -1; } static long mxRational_Hash(mxRationalObject *left) { long hash = left->hash; PyObject *v; if (hash != -1) return hash; /* Use the Python double hash value as basis since this does all the tricks needed to assure that a==b => hash values are equal too (at least in most cases); XXX This is very expensive !!! */ v = mxRational_AsPyFloat((PyObject *)left); if (v == NULL) return -1; hash = PyObject_Hash(v); Py_DECREF(v); left->hash = hash; return hash; } #define mxRational_1x1_Operation(fctname, pyop, apiname) \ static \ PyObject *fctname(PyObject *left) \ { \ PyObject *result; \ \ left = mxRational_FromObject(left); \ if (left == NULL) \ goto onError; \ \ result = mxRational_New(); \ if (result == NULL) \ goto onError; \ \ apiname(((mxRationalObject *)result)->value, \ ((mxRationalObject *)left)->value); \ \ Py_DECREF(left); \ return result; \ \ onError: \ Py_XDECREF(left); \ return NULL; \ } #define mxRational_2x1_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right) \ { \ PyObject *result; \ \ if (redir) { \ if (_mxFloat_Check(left) || _mxFloat_Check(right)) \ return mxNumber_BinaryFloatOperation(pyop, left, right); \ if (PyFloat_Check(left) || PyFloat_Check(right)) \ return mxNumber_BinaryPyFloatOperation(pyop, left, right); \ } \ \ left = mxRational_FromObject(left); \ if (left == NULL) \ return NULL; \ \ right = mxRational_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return NULL; \ } \ \ result = mxRational_New(); \ if (result == NULL) \ goto onError; \ \ apiname(((mxRationalObject *)result)->value, \ ((mxRationalObject *)left)->value, \ ((mxRationalObject *)right)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ return result; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ return result; \ } #define mxRational_2x2_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right) \ { \ PyObject *result1, *result2; \ PyObject *v; \ \ if (redir) { \ if (_mxFloat_Check(left) || _mxFloat_Check(right)) \ return mxNumber_BinaryFloatOperation(pyop, left, right); \ if (PyFloat_Check(left) || PyFloat_Check(right)) \ return mxNumber_BinaryPyFloatOperation(pyop, left, right); \ } \ \ left = mxRational_FromObject(left); \ if (left == NULL) \ return left; \ \ right = mxRational_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return right; \ } \ \ result1 = mxRational_New(); \ if (result1 == NULL) \ goto onError; \ \ result2 = mxRational_New(); \ if (result2 == NULL) { \ Py_DECREF(result1); \ goto onError; \ } \ \ v = PyTuple_New(2); \ if (v == NULL) { \ Py_DECREF(result1); \ Py_DECREF(result2); \ goto onError; \ } \ \ PyTuple_SET_ITEM(v, 0, result1); \ PyTuple_SET_ITEM(v, 1, result2); \ \ apiname(((mxRationalObject *)result1)->value, \ ((mxRationalObject *)result2)->value, \ ((mxRationalObject *)left)->value, \ ((mxRationalObject *)right)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ return v; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ return NULL; \ } #define mxRational_3x1_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right, \ PyObject *extra) \ { \ PyObject *result; \ \ if (redir && \ (PyFloat_Check(left) || PyFloat_Check(right)) || \ PyFloat_Check(extra)) \ return mxNumber_TernaryPyFloatOperation(pyop, left, right, \ extra); \ \ left = mxRational_FromObject(left); \ if (left == NULL) \ return NULL; \ \ right = mxRational_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return NULL; \ } \ \ extra = mxRational_FromObject(extra); \ if (extra == NULL) { \ Py_DECREF(left); \ Py_DECREF(right); \ return NULL; \ } \ \ result = mxRational_New(); \ if (result == NULL) \ goto onError; \ \ apiname(((mxRationalObject *)result)->value, \ ((mxRationalObject *)left)->value, \ ((mxRationalObject *)right)->value, \ ((mxRationalObject *)extra)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ Py_DECREF(extra); \ return result; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ Py_DECREF(extra); \ return result; \ } /* Number Slots */ mxRational_2x1_Operation(mxRational_Add, PyNumber_Add, mpq_add, 1); mxRational_2x1_Operation(mxRational_Subtract, PyNumber_Subtract, mpq_sub, 1); mxRational_2x1_Operation(mxRational_Multiply, PyNumber_Multiply, mpq_mul, 1); mxRational_2x1_Operation(mxRational_Divide, PyNumber_Divide, mpq_div, 1); mxRational_1x1_Operation(mxRational_Negative, PyNumber_Negative, mpq_neg); #if 0 mxRational_1x1_Operation(mxRational_Absolute, PyNumber_Absolute, mpq_abs); mxRational_1x1_Operation(mxRational_Invert, PyNumber_Invert, mpq_com); mxRational_2x1_Operation(mxRational_And, PyNumber_And, mpq_and, 0); mxRational_2x1_Operation(mxRational_Or, PyNumber_Or, mpq_ior, 0); mxRational_2x1_Operation(mxRational_Remainder, PyNumber_Remainder, mpq_tdiv_r, 1); mxRational_2x2_Operation(mxRational_Divmod, PyNumber_Divmod, mpq_tdiv_qr, 1); static PyObject *mxRational_Xor(PyObject *left, PyObject *right) { PyObject *result; mpq_t temp; left = mxRational_FromObject(left); if (left == NULL) return left; right = mxRational_FromObject(right); if (right == NULL) { Py_DECREF(left); return right; } result = mxRational_New(); if (result == NULL) return NULL; /* XOR = (left OR right) AND COM (left AND right) -- SLOW !!! */ mpq_init(temp); mpq_ior(((mxRationalObject *)result)->value, ((mxRationalObject *)left)->value, ((mxRationalObject *)right)->value); mpq_and(temp, ((mxRationalObject *)left)->value, ((mxRationalObject *)right)->value); mpq_com(temp, temp); mpq_and(((mxRationalObject *)result)->value, temp, temp); mpq_clear(temp); Py_DECREF(left); Py_DECREF(right); return result; } static PyObject *mxRational_Power(PyObject *base, PyObject *exp, PyObject *mod) { PyObject *result = NULL; if ((PyFloat_Check(base) || PyFloat_Check(exp)) || PyFloat_Check(mod)) return mxNumber_TernaryPyFloatOperation(PyNumber_Power, base, exp, mod); base = mxRational_FromObject(base); if (base == NULL) return NULL; exp = mxRational_FromObject(exp); if (exp == NULL) { Py_DECREF(base); return NULL; } if (mpq_sgn(((mxRationalObject *)exp)->value) < 0) Py_Error(PyExc_ValueError, "can't raise to a negative power"); result = mxRational_New(); if (result == NULL) goto onError; if (mod == Py_None) { unsigned long uiexp; if (!mpq_fits_ulong_p(((mxRationalObject *)exp)->value)) Py_Error(PyExc_ValueError, "power too large"); uiexp = mpq_get_ui(((mxRationalObject *)exp)->value); mpq_pow_ui(((mxRationalObject *)result)->value, ((mxRationalObject *)base)->value, uiexp); } else { mod = mxRational_FromObject(mod); if (mod == NULL) goto onError; mpq_powm(((mxRationalObject *)result)->value, ((mxRationalObject *)base)->value, ((mxRationalObject *)exp)->value, ((mxRationalObject *)mod)->value); Py_DECREF(mod); } Py_DECREF(base); Py_DECREF(exp); return result; onError: Py_DECREF(base); Py_DECREF(exp); Py_XDECREF(result); return NULL; } #endif static PyObject *mxRational_notimplemented2(PyObject *v, PyObject *w) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } static PyObject *mxRational_notimplemented3(PyObject *v, PyObject *w, PyObject *u) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } /* --- Methods --------------------------------------------------------- */ #define obj ((mxRationalObject*)self) Py_C_Function( mxRational_sign, "sign()\n" "Return the sign of the number.") { Py_NoArgsCheck(); return PyInt_FromLong((long)mpq_sgn(obj->value)); onError: return NULL; } Py_C_Function( mxRational_format, "format(base[, precision])\n" "Return a string representing the Rational in the\n" "given base.\n" "For base10, a precision value >= 0 will return an\n" "approximate decimal point representation of the\n" "Rational, while setting precision to 0 causes the\n" "'nominator/denominator' to be used. precision has no\n" "meaning for non-base10 values") { int precision = 0; int base; Py_Get2Args("i|i:format", base, precision); return mxRational_AsStringObject(self, base, precision); onError: return NULL; } #ifdef COPY_PROTOCOL Py_C_Function( mxRational_copy, "copy([memo])\n\n" "Return a new reference for the instance. This function\n" "is used for the copy-protocol. Real copying doesn't take\n" "place, since the instances are immutable.") { PyObject *memo; Py_GetArg("|O",memo); Py_INCREF(obj); return (PyObject *)obj; onError: return NULL; } #endif #undef obj /* --- Python Type Tables ---------------------------------------------- */ static PyNumberMethods mxRational_TypeAsNumber = { /* These slots are not NULL-checked, so we must provide dummy functions */ (binaryfunc)mxRational_Add, /*nb_add*/ (binaryfunc)mxRational_Subtract, /*nb_subtract*/ (binaryfunc)mxRational_Multiply, /*nb_multiply*/ (binaryfunc)mxRational_Divide, /*nb_divide*/ (binaryfunc)notimplemented2, /*nb_remainder*/ (binaryfunc)notimplemented2, /*nb_divmod*/ (ternaryfunc)notimplemented3, /*nb_power*/ (unaryfunc)mxRational_Negative, /*nb_negative*/ (unaryfunc)notimplemented1, /*nb_positive*/ /* Everything below this line EXCEPT nb_nonzero (!) is NULL checked */ (unaryfunc)0, /*nb_absolute*/ (inquiry)mxRational_NonZero, /*nb_nonzero*/ (unaryfunc)0, /*nb_invert*/ (binaryfunc)0, /*nb_lshift*/ (binaryfunc)0, /*nb_rshift*/ (binaryfunc)0, /*nb_and*/ (binaryfunc)0, /*nb_xor*/ (binaryfunc)0, /*nb_or*/ (coercion)mxRational_Coerce, /*nb_coerce*/ (unaryfunc)mxRational_AsPyInt, /*nb_int*/ (unaryfunc)mxRational_AsPyLong, /*nb_long*/ (unaryfunc)mxRational_AsPyFloat, /*nb_float*/ (unaryfunc)0, /*nb_oct*/ (unaryfunc)0, /*nb_hex*/ }; statichere PyTypeObject mxRational_Type = { PyObject_HEAD_INIT(0) /* init at startup ! */ 0, /*ob_size*/ "Rational", /*tp_name*/ sizeof(mxRationalObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* slots */ (destructor)mxRational_Free, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ (getattrfunc)mxRational_Getattr, /*tp_getattr*/ (setattrfunc)0, /*tp_setattr*/ (cmpfunc)mxRational_Compare, /*tp_compare*/ (reprfunc)mxRational_Repr, /*tp_repr*/ &mxRational_TypeAsNumber, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)mxRational_Hash, /*tp_hash*/ (ternaryfunc)0, /*tp_call*/ (reprfunc)mxRational_Str, /*tp_str*/ (getattrofunc)0, /*tp_getattro*/ (setattrofunc)0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_CHECKTYPES, /*tp_flags*/ (char*) 0 /*tp_doc*/ }; /* Python Method Table */ statichere PyMethodDef mxRational_Methods[] = { Py_MethodListEntryNoArgs("sign",mxRational_sign), Py_MethodListEntry("format",mxRational_format), #ifdef COPY_PROTOCOL Py_MethodListEntry("copy",mxRational_copy), #endif {NULL,NULL} /* end of list */ }; /* === mxFloat Object ================================================ */ /* If precision is negative, then the current default precision is used as precision. */ static PyObject *mxFloat_New(int precision) { mxFloatObject *obj; #ifdef MXNUMBER_FREELIST if (mxFloat_FreeList) { obj = mxFloat_FreeList; mxFloat_FreeList = *(mxFloatObject **)mxFloat_FreeList; obj->ob_type = &mxFloat_Type; _Py_NewReference(obj); } else #endif { obj = PyObject_NEW(mxFloatObject,&mxFloat_Type); if (obj == NULL) return NULL; } /* Init vars */ if (precision < 0) precision = mxFloat_default_precision; mpf_init2(obj->value, (unsigned long)precision); obj->hash = -1; return (PyObject *)obj; onError: return NULL; } static void mxFloat_Free(mxFloatObject *obj) { if (obj == NULL) return; /* Free vars */ mpf_clear(obj->value); #ifdef MXNUMBER_FREELIST /* Append to free list */ *(mxFloatObject **)obj = mxFloat_FreeList; mxFloat_FreeList = obj; #else PyObject_Del(obj); #endif } /* --- Internal functions ---------------------------------------------- */ /* --- API functions --------------------------------------------------- */ static PyObject *mxFloat_FromMPZ(mpz_t value) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set_z(obj->value, value); return (PyObject *)obj; } static PyObject *mxFloat_FromMPQ(mpq_t value) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set_q(obj->value, value); return (PyObject *)obj; } static PyObject *mxFloat_FromLong(long value) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set_si(obj->value, value); return (PyObject *)obj; } static PyObject *mxFloat_FromDouble(double value) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set_d(obj->value, value); return (PyObject *)obj; } static PyObject *_mxFloat_FromInteger(mxIntegerObject *other) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set_z(obj->value, other->value); return (PyObject *)obj; } static PyObject *_mxFloat_FromFloat(mxFloatObject *other) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set(obj->value, other->value); return (PyObject *)obj; } static PyObject *_mxFloat_FromRational(mxRationalObject *other) { mxFloatObject *obj; obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; mpf_set_q(obj->value, other->value); return (PyObject *)obj; } static PyObject *mxFloat_FromString(char *value, int base) { mxFloatObject *obj; if (value == NULL) { PyErr_BadInternalCall(); return NULL; } obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; /* XXX Add strict string format checking... (see Integers) */ if (mpf_set_str(obj->value, value, base)) Py_Error(mxNumber_Error, "could not convert string to Float"); return (PyObject *)obj; onError: mxFloat_Free(obj); return NULL; } static PyObject *mxFloat_FromPyLong(PyObject *value) { mxFloatObject *obj = NULL; PyObject *v = NULL; if (value == NULL || !PyLong_Check(value)) { PyErr_BadInternalCall(); return NULL; } obj = (mxFloatObject *)mxFloat_New(-1); if (obj == NULL) return NULL; v = PyObject_Str(value); if (v == NULL) goto onError; Py_Assert(PyString_Check(v), PyExc_TypeError, "__str__ must return a string object"); if (mpf_set_str(obj->value, PyString_AS_STRING(v), 0)) Py_Error(mxNumber_Error, "could not convert long to Float"); return (PyObject *)obj; onError: if (obj) mxFloat_Free(obj); Py_XDECREF(v); return NULL; } statichere PyObject *mxFloat_FromObject(PyObject *value) { PyObject *v; if (value == NULL) { PyErr_BadInternalCall(); return NULL; } else if (_mxFloat_Check(value)) { Py_INCREF(value); return value; } else if (PyInt_Check(value)) return mxFloat_FromLong(PyInt_AS_LONG(value)); else if (PyString_Check(value)) /* Determine the base by looking at the string prefix */ return mxFloat_FromString(PyString_AS_STRING(value), 0); else if (PyFloat_Check(value)) return mxFloat_FromDouble(PyFloat_AS_DOUBLE(value)); else if (_mxRational_Check(value)) return _mxFloat_FromRational((mxRationalObject *)value); else if (PyLong_Check(value)) return mxFloat_FromPyLong(value); /* Try to convert via __long__ method */ v = PyNumber_Long(value); if (v == NULL) Py_Error(PyExc_TypeError, "can't convert object to mx.Number.Float"); return mxFloat_FromPyLong(v); onError: return NULL; } static long mxFloat_AsLong(PyObject *obj) { double x; if (obj == NULL || !_mxFloat_Check(obj)) { PyErr_BadInternalCall(); return -1; } /* Convert via C double */ x = mpf_get_d(((mxFloatObject *)obj)->value); if (x > LONG_MAX || x < LONG_MIN) Py_Error(PyExc_OverflowError, "Float cannot be converted to a Python integer"); /* XXX What about rounding and negative values ??? */ return (long)x; onError: return -1; } static double mxFloat_AsDouble(PyObject *obj) { if (obj == NULL || !_mxFloat_Check(obj)) { PyErr_BadInternalCall(); return -1.0; } return mpf_get_d(((mxFloatObject *)obj)->value); } /* Convert a Float to a string using the given precision. If precision is > 0, the Float will be formatted using at most precision significant digits. Setting precision to 0 causes the maximum number of significant digits to be chosen depending on the precision used for storing the floating point value. */ static PyObject *mxFloat_AsStringObject(PyObject *obj, int precision) { char *buffer = NULL; char *p; int digits; PyObject *v; mp_exp_t exp; if (obj == NULL || !_mxFloat_Check(obj)) { PyErr_BadInternalCall(); return NULL; } if (precision == 0) { /* String format: base10 "d.ddddd...e+00" */ buffer = mpf_get_str(NULL, &exp, 10, 0, ((mxFloatObject *)obj)->value); if (buffer == NULL) Py_Error(mxNumber_Error, "conversion to string failed"); if ((int)exp < -9999998 || (int)exp > 10000000) Py_Error(mxNumber_Error, "exponent too large to convert to string"); digits = strlen(buffer); p = (char *)realloc(buffer, digits + 10); if (p == NULL) { PyErr_NoMemory(); goto onError; } buffer = p; /* Skip sign in processing */ if (*p == '-') { p++; digits--; } /* Make room for the decimal point */ if (digits > 1) memmove(p + 2, p + 1, digits - 1); else { if (digits == 0) { *p = '0'; digits++; exp = 1; } *(p + 2) = '0'; digits++; } /* Add decimal point and exponent (max. 10 extra chars) */ *(p + 1) = '.'; p = p + digits + 1; *p++ = 'e'; sprintf(p, "%+02i", (int)exp - 1); } else { double value; int len, size; /* String format: %g with the given precision using the same strategy as is used for Python floats */ value = mpf_get_d(((mxFloatObject *)obj)->value); size = precision + 10; buffer = PyMem_NEW(char, size); if (buffer == NULL) return PyErr_NoMemory(); len = sprintf(buffer, "%.*g", precision, value); if (len <= 0) Py_Error(PyExc_TypeError, "could not stringify Rational"); if (len >= size) Py_Error(PyExc_SystemError, "buffer overrun in str(Rational)"); /* Make sure that the generated string includes a decimal point or other "float" indicator */ for (p = buffer; *p != '\0'; p++) if (*p == '.') break; if (*p == '\0') { *p++ = '.'; *p++ = '0'; *p++ = '\0'; } } v = PyString_FromString(buffer); free(buffer); return v; onError: if (buffer) free(buffer); return NULL; } static PyObject *mxFloat_AsPyLong(PyObject *obj) { char *buffer = NULL; PyObject *v; #if 0 mp_exp_t exp; #endif if (obj == NULL || !_mxFloat_Check(obj)) { PyErr_BadInternalCall(); return NULL; } #if 0 buffer = mpf_get_str(NULL, &exp, 36, 0, ((mxFloatObject *)obj)->value); #else buffer = NULL; #endif if (buffer == NULL) Py_Error(mxNumber_Error, "conversion to long not implemented yet"); /* XXX Add exponent information somewhere... */ v = PyLong_FromString(buffer, NULL, 36); free(buffer); return v; onError: if (buffer) free(buffer); return NULL; } /* --- Slots ----------------------------------------------------------- */ static PyObject *mxFloat_AsPyFloat(PyObject *obj) { double value = mxFloat_AsDouble(obj); return PyFloat_FromDouble(value); } static PyObject *mxFloat_AsPyInt(PyObject *obj) { long value = mxFloat_AsLong(obj); if (value == -1 && PyErr_Occurred()) return NULL; return PyInt_FromLong(value); } static int mxFloat_NonZero(PyObject *obj) { int i = mpf_sgn(((mxFloatObject *)obj)->value); return i != 0; } static PyObject *mxFloat_Str(PyObject *obj) { return mxFloat_AsStringObject(obj, 17); } static PyObject *mxFloat_Repr(PyObject *obj) { return mxFloat_AsStringObject(obj, 0); } static PyObject *mxFloat_Getattr(PyObject *obj, char *name) { if (Py_WantAttr(name,"precision")) return PyInt_FromLong(mpf_get_prec(((mxFloatObject *)obj)->value)); else if (Py_WantAttr(name,"__members__")) return Py_BuildValue("[s]", "precision" ); return Py_FindMethod(mxFloat_Methods, obj, name); onError: return NULL; } static int mxFloat_Coerce(PyObject **left, PyObject **right) { if (left == right) { Py_INCREF(*left); Py_INCREF(*right); return 0; } /* Coerce to Floats */ *left = mxFloat_FromObject(*left); if (*left == NULL) goto onError; *right = mxFloat_FromObject(*right); if (*right == NULL) { Py_DECREF(*left); goto onError; } return 0; onError: /* XXX Should perhaps clear the exception and return 1 instead ?! */ return -1; } static int mxFloat_Compare(PyObject *left, PyObject *right) { int rc; if (left == right) return 0; /* Short-cut */ if (_mxFloat_Check(left) && _mxFloat_Check(right)) return mpf_cmp(((mxFloatObject *)left)->value, ((mxFloatObject *)right)->value); left = mxFloat_FromObject(left); if (left == NULL) goto onError; right = mxFloat_FromObject(right); if (right == NULL) { Py_DECREF(left); goto onError; } rc = mpf_cmp(((mxFloatObject *)left)->value, ((mxFloatObject *)right)->value); Py_DECREF(left); Py_DECREF(right); return rc; onError: return -1; } static long mxFloat_Hash(mxFloatObject *left) { long hash = left->hash; PyObject *v; if (hash != -1) return hash; /* Use the Python float hash value as basis since this does all the tricks needed to assure that a==b => hash values are equal too (at least in most cases); XXX This is very expensive !!! */ v = mxFloat_AsPyFloat((PyObject *)left); if (v == NULL) return -1; hash = PyObject_Hash(v); Py_DECREF(v); left->hash = hash; return hash; } #define mxFloat_1x1_Operation(fctname, pyop, apiname) \ static \ PyObject *fctname(PyObject *left) \ { \ PyObject *result; \ \ left = mxFloat_FromObject(left); \ if (left == NULL) \ goto onError; \ \ result = mxFloat_New(-1); \ if (result == NULL) \ goto onError; \ \ apiname(((mxFloatObject *)result)->value, \ ((mxFloatObject *)left)->value); \ \ Py_DECREF(left); \ return result; \ \ onError: \ Py_XDECREF(left); \ return NULL; \ } #define mxFloat_2x1_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right) \ { \ PyObject *result; \ \ left = mxFloat_FromObject(left); \ if (left == NULL) \ return NULL; \ \ right = mxFloat_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return NULL; \ } \ \ result = mxFloat_New(-1); \ if (result == NULL) \ goto onError; \ \ apiname(((mxFloatObject *)result)->value, \ ((mxFloatObject *)left)->value, \ ((mxFloatObject *)right)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ return result; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ return result; \ } #define mxFloat_2x2_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right) \ { \ PyObject *result1, *result2; \ PyObject *v; \ \ left = mxFloat_FromObject(left); \ if (left == NULL) \ return left; \ \ right = mxFloat_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return right; \ } \ \ result1 = mxFloat_New(-1); \ if (result1 == NULL) \ goto onError; \ \ result2 = mxFloat_New(-1); \ if (result2 == NULL) { \ Py_DECREF(result1); \ goto onError; \ } \ \ v = PyTuple_New(2); \ if (v == NULL) { \ Py_DECREF(result1); \ Py_DECREF(result2); \ goto onError; \ } \ \ PyTuple_SET_ITEM(v, 0, result1); \ PyTuple_SET_ITEM(v, 1, result2); \ \ apiname(((mxFloatObject *)result1)->value, \ ((mxFloatObject *)result2)->value, \ ((mxFloatObject *)left)->value, \ ((mxFloatObject *)right)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ return v; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ return NULL; \ } #define mxFloat_3x1_Operation(fctname, pyop, apiname, redir) \ static \ PyObject *fctname(PyObject *left, \ PyObject *right, \ PyObject *extra) \ { \ PyObject *result; \ \ left = mxFloat_FromObject(left); \ if (left == NULL) \ return NULL; \ \ right = mxFloat_FromObject(right); \ if (right == NULL) { \ Py_DECREF(left); \ return NULL; \ } \ \ extra = mxFloat_FromObject(extra); \ if (extra == NULL) { \ Py_DECREF(left); \ Py_DECREF(right); \ return NULL; \ } \ \ result = mxFloat_New(-1); \ if (result == NULL) \ goto onError; \ \ apiname(((mxFloatObject *)result)->value, \ ((mxFloatObject *)left)->value, \ ((mxFloatObject *)right)->value, \ ((mxFloatObject *)extra)->value); \ \ Py_DECREF(left); \ Py_DECREF(right); \ Py_DECREF(extra); \ return result; \ \ onError: \ Py_DECREF(left); \ Py_DECREF(right); \ Py_DECREF(extra); \ return result; \ } /* Number Slots */ mxFloat_2x1_Operation(mxFloat_Add, PyNumber_Add, mpf_add, 1); mxFloat_2x1_Operation(mxFloat_Subtract, PyNumber_Subtract, mpf_sub, 1); mxFloat_2x1_Operation(mxFloat_Multiply, PyNumber_Multiply, mpf_mul, 1); mxFloat_2x1_Operation(mxFloat_Divide, PyNumber_Divide, mpf_div, 1); mxFloat_1x1_Operation(mxFloat_Negative, PyNumber_Negative, mpf_neg); mxFloat_1x1_Operation(mxFloat_Absolute, PyNumber_Absolute, mpf_abs); #if 0 mxFloat_1x1_Operation(mxFloat_Invert, PyNumber_Invert, mpf_com); mxFloat_2x1_Operation(mxFloat_And, PyNumber_And, mpf_and, 0); mxFloat_2x1_Operation(mxFloat_Or, PyNumber_Or, mpf_ior, 0); mxFloat_2x1_Operation(mxFloat_Remainder, PyNumber_Remainder, mpf_tdiv_r, 1); mxFloat_2x2_Operation(mxFloat_Divmod, PyNumber_Divmod, mpf_tdiv_qr, 1); static PyObject *mxFloat_Xor(PyObject *left, PyObject *right) { PyObject *result; mpf_t temp; left = mxFloat_FromObject(left); if (left == NULL) return left; right = mxFloat_FromObject(right); if (right == NULL) { Py_DECREF(left); return right; } result = mxFloat_New(-1); if (result == NULL) return NULL; /* XOR = (left OR right) AND COM (left AND right) -- SLOW !!! */ mpf_init(temp); mpf_ior(((mxFloatObject *)result)->value, ((mxFloatObject *)left)->value, ((mxFloatObject *)right)->value); mpf_and(temp, ((mxFloatObject *)left)->value, ((mxFloatObject *)right)->value); mpf_com(temp, temp); mpf_and(((mxFloatObject *)result)->value, temp, temp); mpf_clear(temp); Py_DECREF(left); Py_DECREF(right); return result; } static PyObject *mxFloat_Power(PyObject *base, PyObject *exp, PyObject *mod) { PyObject *result = NULL; if ((PyFloat_Check(base) || PyFloat_Check(exp)) || PyFloat_Check(mod)) return mxNumber_TernaryPyFloatOperation(PyNumber_Power, base, exp, mod); base = mxFloat_FromObject(base); if (base == NULL) return NULL; exp = mxFloat_FromObject(exp); if (exp == NULL) { Py_DECREF(base); return NULL; } if (mpf_sgn(((mxFloatObject *)exp)->value) < 0) Py_Error(PyExc_ValueError, "can't raise to a negative power"); result = mxFloat_New(-1); if (result == NULL) goto onError; if (mod == Py_None) { unsigned long uiexp; if (!mpf_fits_ulong_p(((mxFloatObject *)exp)->value)) Py_Error(PyExc_ValueError, "power too large"); uiexp = mpf_get_ui(((mxFloatObject *)exp)->value); mpf_pow_ui(((mxFloatObject *)result)->value, ((mxFloatObject *)base)->value, uiexp); } else { mod = mxFloat_FromObject(mod); if (mod == NULL) goto onError; mpf_powm(((mxFloatObject *)result)->value, ((mxFloatObject *)base)->value, ((mxFloatObject *)exp)->value, ((mxFloatObject *)mod)->value); Py_DECREF(mod); } Py_DECREF(base); Py_DECREF(exp); return result; onError: Py_DECREF(base); Py_DECREF(exp); Py_XDECREF(result); return NULL; } #endif static PyObject *mxFloat_notimplemented2(PyObject *v, PyObject *w) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } static PyObject *mxFloat_notimplemented3(PyObject *v, PyObject *w, PyObject *u) { Py_Error(PyExc_TypeError, "operation not implemented"); onError: return NULL; } /* --- Methods --------------------------------------------------------- */ #define obj ((mxFloatObject*)self) Py_C_Function( mxFloat_sign, "sign()\n" "Return the sign of the Float.") { Py_NoArgsCheck(); return PyInt_FromLong((long)mpf_sgn(obj->value)); onError: return NULL; } Py_C_Function( mxFloat_format, "format(precision)\n" "Return a string representing the Float.\n" "precision defines the maximum number of significant\n" "digits to use, 0 means to let the implementation\n" "choose the value depending on the Float's storage\n" "precision.") { int precision; Py_GetArg("i:format", precision); return mxFloat_AsStringObject(self, precision); onError: return NULL; } #ifdef COPY_PROTOCOL Py_C_Function( mxFloat_copy, "copy([memo])\n\n" "Return a new reference for the instance. This function\n" "is used for the copy-protocol. Real copying doesn't take\n" "place, since the instances are immutable.") { PyObject *memo; Py_GetArg("|O",memo); Py_INCREF(obj); return (PyObject *)obj; onError: return NULL; } #endif #undef obj /* --- Python Type Tables ---------------------------------------------- */ static PyNumberMethods mxFloat_TypeAsNumber = { /* These slots are not NULL-checked, so we must provide dummy functions */ (binaryfunc)mxFloat_Add, /*nb_add*/ (binaryfunc)mxFloat_Subtract, /*nb_subtract*/ (binaryfunc)mxFloat_Multiply, /*nb_multiply*/ (binaryfunc)mxFloat_Divide, /*nb_divide*/ (binaryfunc)notimplemented2, /*nb_remainder*/ (binaryfunc)notimplemented2, /*nb_divmod*/ (ternaryfunc)notimplemented3, /*nb_power*/ (unaryfunc)mxFloat_Negative, /*nb_negative*/ (unaryfunc)notimplemented1, /*nb_positive*/ /* Everything below this line EXCEPT nb_nonzero (!) is NULL checked */ (unaryfunc)mxFloat_Absolute, /*nb_absolute*/ (inquiry)mxFloat_NonZero, /*nb_nonzero*/ (unaryfunc)0, /*nb_invert*/ (binaryfunc)0, /*nb_lshift*/ (binaryfunc)0, /*nb_rshift*/ (binaryfunc)0, /*nb_and*/ (binaryfunc)0, /*nb_xor*/ (binaryfunc)0, /*nb_or*/ (coercion)mxFloat_Coerce, /*nb_coerce*/ (unaryfunc)mxFloat_AsPyInt, /*nb_int*/ (unaryfunc)mxFloat_AsPyLong, /*nb_long*/ (unaryfunc)mxFloat_AsPyFloat, /*nb_float*/ (unaryfunc)0, /*nb_oct*/ (unaryfunc)0, /*nb_hex*/ }; statichere PyTypeObject mxFloat_Type = { PyObject_HEAD_INIT(0) /* init at startup ! */ 0, /*ob_size*/ "Float", /*tp_name*/ sizeof(mxFloatObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* slots */ (destructor)mxFloat_Free, /*tp_dealloc*/ (printfunc)0, /*tp_print*/ (getattrfunc)mxFloat_Getattr, /*tp_getattr*/ (setattrfunc)0, /*tp_setattr*/ (cmpfunc)mxFloat_Compare, /*tp_compare*/ (reprfunc)mxFloat_Repr, /*tp_repr*/ &mxFloat_TypeAsNumber, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)mxFloat_Hash, /*tp_hash*/ (ternaryfunc)0, /*tp_call*/ (reprfunc)mxFloat_Str, /*tp_str*/ (getattrofunc)0, /*tp_getattro*/ (setattrofunc)0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_CHECKTYPES, /*tp_flags*/ (char*) 0 /*tp_doc*/ }; /* Python Method Table */ statichere PyMethodDef mxFloat_Methods[] = { Py_MethodListEntryNoArgs("sign",mxFloat_sign), Py_MethodListEntry("format",mxFloat_format), #ifdef COPY_PROTOCOL Py_MethodListEntry("copy",mxFloat_copy), #endif {NULL,NULL} /* end of list */ }; /* --- Generic APIs -------------------------------------------------------- */ /* Coerce value to a Python float object */ statichere PyObject *mxNumber_AsPyFloat(PyObject *value) { if (PyFloat_Check(value)) { Py_INCREF(value); return value; } else if (_mxInteger_Check(value)) return mxInteger_AsPyFloat(value); else if (_mxRational_Check(value)) return mxRational_AsPyFloat(value); else Py_Error(PyExc_TypeError, "can't convert object to a Python float"); onError: return NULL; } /* Execute a binary operation by first coercing both arguments to Python floats. */ statichere PyObject *mxNumber_BinaryPyFloatOperation(binaryfunc binop, PyObject *left, PyObject *right) { PyObject *v; left = mxNumber_AsPyFloat(left); if (left == NULL) return NULL; right = mxNumber_AsPyFloat(right); if (right == NULL) { Py_DECREF(left); return NULL; } v = binop(left, right); Py_DECREF(left); Py_DECREF(right); return v; } /* Execute a ternary operation by first coercing all arguments to Python floats. The extra argument may be None. */ statichere PyObject *mxNumber_TernaryPyFloatOperation(ternaryfunc ternop, PyObject *left, PyObject *right, PyObject *extra) { PyObject *v; left = mxNumber_AsPyFloat(left); if (left == NULL) return NULL; right = mxNumber_AsPyFloat(right); if (right == NULL) { Py_DECREF(left); return NULL; } if (extra != Py_None) { extra = mxNumber_AsPyFloat(extra); if (extra == NULL) { Py_DECREF(left); Py_DECREF(right); return NULL; } } else Py_INCREF(extra); v = ternop(left, right, extra); Py_DECREF(left); Py_DECREF(right); Py_DECREF(extra); return v; } /* Execute a binary operation by first coercing both arguments to Integers. */ statichere PyObject *mxNumber_BinaryIntegerOperation(binaryfunc binop, PyObject *left, PyObject *right) { PyObject *v; left = mxInteger_FromObject(left); if (left == NULL) return NULL; right = mxInteger_FromObject(right); if (right == NULL) { Py_DECREF(left); return NULL; } v = binop(left, right); Py_DECREF(left); Py_DECREF(right); return v; } /* Execute a binary operation by first coercing both arguments to Rationals. */ statichere PyObject *mxNumber_BinaryRationalOperation(binaryfunc binop, PyObject *left, PyObject *right) { PyObject *v; left = mxRational_FromObject(left); if (left == NULL) return NULL; right = mxRational_FromObject(right); if (right == NULL) { Py_DECREF(left); return NULL; } v = binop(left, right); Py_DECREF(left); Py_DECREF(right); return v; } /* Execute a binary operation by first coercing both arguments to Floats. */ statichere PyObject *mxNumber_BinaryFloatOperation(binaryfunc binop, PyObject *left, PyObject *right) { PyObject *v; left = mxFloat_FromObject(left); if (left == NULL) return NULL; right = mxFloat_FromObject(right); if (right == NULL) { Py_DECREF(left); return NULL; } v = binop(left, right); Py_DECREF(left); Py_DECREF(right); return v; } /* --- Module functions -------------------------------------------------- */ Py_C_Function( mxNumber_Integer, "Integer(value)\n\n" "Returns an Integer-object reflecting the given value." ) { PyObject *value; Py_GetArg("O", value); return mxInteger_FromObject(value); onError: return NULL; } Py_C_Function( mxNumber_Rational, "Rational(value,denominator=1)\n\n" "Returns a Rational-object reflecting the given value.\n" "If denominator is given, value is interpreted as numerator." ) { PyObject *value; PyObject *denominator = NULL; Py_Get2Args("O|O", value, denominator); if (denominator == NULL) return mxRational_FromObject(value); else return mxRational_FromTwoObjects(value, denominator); onError: return NULL; } Py_C_Function( mxNumber_Float, "Float(value[,precision])\n\n" "Returns a Float-object reflecting the given value.\n" "precision gives the number of bits which should at least\n" "be used to store the floating point value. If not given,\n" "the default precision is used." ) { PyObject *value, *result; int precision = -1; int old_precision; Py_Get2Args("O|i", value, precision); /* XXX This should probably be handled in a more thread-safe way... */ old_precision = mxFloat_default_precision; if (precision >= 0) mxFloat_default_precision = precision; result = mxFloat_FromObject(value); mxFloat_default_precision = old_precision; return result; onError: return NULL; } Py_C_Function( mxNumber_FareyRational, "FareyRational(value, maxden)\n\n" "Returns a Rational-object reflecting the given value\n" "and using maxden as maximum denominator.\n" ) { PyObject *value, *maxden; Py_Get2Args("OO", value, maxden); return mxRational_FromFareyApprox(value, maxden); onError: return NULL; } Py_C_Function( mxNumber_Factorial, "Factorial(value)\n" "Return the factorial of the value as Integer-object.") { PyObject *result; unsigned long n; Py_GetArg("l", n); Py_Assert(n >= 0, PyExc_ValueError, "number must be positive"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_fac_ui(((mxIntegerObject *)result)->value, n); return result; onError: return NULL; } Py_C_Function( mxNumber_Binomial, "Binomial(n, k)\n" "Return the binomial coefficient n over k as Integer-object.") { PyObject *result; unsigned long n, k; Py_Get2Args("ll", n, k); Py_Assert(n >= 0 && k >= 0, PyExc_ValueError, "arguments must be non-negative"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_bin_uiui(((mxIntegerObject *)result)->value, n, k); return result; onError: return NULL; } Py_C_Function( mxNumber_Fibonacci, "Fibonacci(n)\n" "Return the n-th Fibonacci number as Integer-object.") { PyObject *result; unsigned long n; Py_GetArg("l", n); Py_Assert(n >= 0, PyExc_ValueError, "number must be positive"); result = mxInteger_New(); if (result == NULL) return NULL; mpz_fib_ui(((mxIntegerObject *)result)->value, n); return result; onError: return NULL; } /* --- Module interface ------------------------------------------------- */ /* Python Method Table */ static PyMethodDef Module_methods[] = { Py_MethodListEntry("Integer",mxNumber_Integer), Py_MethodListEntry("Rational",mxNumber_Rational), Py_MethodListEntry("Float",mxNumber_Float), Py_MethodListEntry("FareyRational",mxNumber_FareyRational), Py_MethodListEntry("Factorial",mxNumber_Factorial), Py_MethodListEntry("Binomial",mxNumber_Binomial), Py_MethodListEntry("Fibonacci",mxNumber_Fibonacci), {NULL,NULL} /* end of list */ }; /* C API table - always add new things to the end for binary compatibility. */ static mxNumberModule_APIObject mxNumberModuleAPI = { &mxInteger_Type, &mxRational_Type, &mxFloat_Type }; /* Cleanup function */ static void mxNumberModule_Cleanup(void) { #ifdef MXNUMBER_FREELIST { mxIntegerObject *d = mxInteger_FreeList; while (d != NULL) { mxIntegerObject *v = d; d = *(mxIntegerObject **)d; PyObject_Del(v); } } { mxRationalObject *d = mxRational_FreeList; while (d != NULL) { mxRationalObject *v = d; d = *(mxRationalObject **)d; PyObject_Del(v); } } { mxFloatObject *d = mxFloat_FreeList; while (d != NULL) { mxFloatObject *v = d; d = *(mxFloatObject **)d; PyObject_Del(v); } } #endif mpz_clear(max_slong); mpz_clear(min_slong); /* Reset mxNumber_Initialized flag */ mxNumber_Initialized = 0; } /* Create PyMethodObjects and register them in the module's dict */ MX_EXPORT(void) initmxNumber(void) { PyObject *module, *moddict; if (mxNumber_Initialized) Py_Error(PyExc_SystemError, "can't initialize "MXNUMBER_MODULE" more than once"); /* Init type objects */ PyType_Init(mxInteger_Type); PyType_Init(mxRational_Type); PyType_Init(mxFloat_Type); /* Init globals */ mpz_init(max_slong); mpz_init(min_slong); mpz_set_si(max_slong, LONG_MAX); mpz_set_si(min_slong, LONG_MIN); #ifdef MXNUMBER_FREELIST mxInteger_FreeList = NULL; mxRational_FreeList = NULL; mxFloat_FreeList = NULL; #endif /* Create module */ module = Py_InitModule4(MXNUMBER_MODULE, /* Module name */ Module_methods, /* Method list */ Module_docstring, /* Module doc-string */ (PyObject *)NULL, /* always pass this as *self */ PYTHON_API_VERSION); /* API Version */ if (module == NULL) goto onError; /* Register cleanup function */ if (Py_AtExit(mxNumberModule_Cleanup)) { /* XXX what to do if we can't register that function ??? */ DPRINTF("* Failed to register mxNumber cleanup function\n"); } /* Add some constants to the module's dict */ moddict = PyModule_GetDict(module); if (moddict == NULL) goto onError; insobj(moddict,"__version__",PyString_FromString(MXNUMBER_VERSION)); /* Errors */ if (!(mxNumber_Error = insexc(moddict,"Error",PyExc_StandardError))) goto onError; /* Type objects */ Py_INCREF(&mxInteger_Type); PyDict_SetItemString(moddict,"IntegerType", (PyObject *)&mxInteger_Type); Py_INCREF(&mxRational_Type); PyDict_SetItemString(moddict,"RationalType", (PyObject *)&mxRational_Type); Py_INCREF(&mxFloat_Type); PyDict_SetItemString(moddict,"FloatType", (PyObject *)&mxFloat_Type); /* Export C API */ insobj(moddict,MXNUMBER_MODULE"API", PyCObject_FromVoidPtr((void *)&mxNumberModuleAPI, NULL)); DPRINTF("* Loaded "MXNUMBER_MODULE" C extension at 0x%0x.\n", (int)module); /* We are now initialized */ mxNumber_Initialized = 1; onError: /* Check for errors and report them */ if (PyErr_Occurred()) Py_ReportModuleInitError(MXNUMBER_MODULE); return; }