[Python-checkins] r46074 - sandbox/trunk/decimal-c sandbox/trunk/decimal-c/_decimal.c sandbox/trunk/decimal-c/decimal.h
georg.brandl
python-checkins at python.org
Mon May 22 17:47:16 CEST 2006
Author: georg.brandl
Date: Mon May 22 17:47:15 2006
New Revision: 46074
Added:
sandbox/trunk/decimal-c/
sandbox/trunk/decimal-c/_decimal.c
sandbox/trunk/decimal-c/decimal.h
Log:
Import first skeleton of C decimal module.
Added: sandbox/trunk/decimal-c/_decimal.c
==============================================================================
--- (empty file)
+++ sandbox/trunk/decimal-c/_decimal.c Mon May 22 17:47:15 2006
@@ -0,0 +1,1942 @@
+/* C implementation of the decimal module.
+ */
+
+#include "Python.h"
+#include "modsupport.h"
+#include "structmember.h"
+#include "decimal.h"
+
+/* helpful macros ************************************************************/
+
+#define MODULE_NAME "_decimal"
+#define INITFUNC_NAME init_decimal
+
+#define contextobject PyDecimalContextObject
+#define decimalobject PyDecimalObject
+
+#define ISFLAGSET(f, b) (((f) >> b) & 1)
+#define SETFLAG(f, b) (f |= (1 << b))
+
+/* Checks */
+
+#define ISSPECIAL(op) ((op)->sign >= SIGN_POSINF)
+#define GETNAN(op) ( ( (op)->sign == SIGN_POSNAN || (op)->sign == SIGN_NEGNAN ) ? \
+ 1 : ( ( (op)->sign == SIGN_POSSNAN || (op)->sign == SIGN_NEGSNAN ) ? 2 : 0 ) )
+#define ISINF(op) ((op)->sign == SIGN_POSINF || (op)->sign == SIGN_NEGINF)
+
+/* Exponent calculations */
+
+/* XXX: overflow? */
+#define ETINY(ctx) ((ctx)->Emin - (ctx)->prec + 1)
+#define ETOP(ctx) ((ctx)->Emax - (ctx)->prec + 1)
+#define ADJUSTED(dec) ((dec)->exp + (dec)->ob_size - 1)
+
+/* constant values ***********************************************************/
+
+/* sign values */
+
+#define SIGN_POS 0
+#define SIGN_NEG 1
+
+#define SIGN_POSINF 2
+#define SIGN_NEGINF 3
+#define SIGN_POSNAN 4
+#define SIGN_NEGNAN 5
+#define SIGN_POSSNAN 6
+#define SIGN_NEGSNAN 7
+
+
+/* Rounding constants */
+
+#define ROUND_DOWN 0
+#define ROUND_UP 1
+#define ROUND_HALF_DOWN 2
+#define ROUND_HALF_EVEN 3
+#define ROUND_HALF_UP 4
+#define ROUND_FLOOR 5
+#define ROUND_CEILING 6
+
+#define ALWAYS_ROUND 0
+#define NEVER_ROUND 16
+
+/* Context signals */
+
+#define NUMSIGNALS 8
+
+#define S_CLAMPED 0
+#define S_INV_OPERATION 1
+#define S_DIV_BY_ZERO 2
+#define S_INEXACT 3
+#define S_ROUNDED 4
+#define S_OVERFLOW 5
+#define S_UNDERFLOW 6
+#define S_SUBNORMAL 7
+
+/* Other conditions (for context_raise_error) */
+
+#define NUMCONDITIONS 4
+
+#define C_DIV_IMPOSSIBLE 8
+#define C_DIV_UNDEFINED 9
+#define C_INV_CONTEXT 10
+#define C_CONV_SYNTAX 11
+
+
+/* Exceptions ****************************************************************/
+
+/* Indentation below hints at inheritance. */
+
+static PyObject *DecimalException;
+static PyObject *Clamped;
+static PyObject *InvalidOperation;
+static PyObject *ConversionSyntax;
+static PyObject *DivisionImpossible;
+static PyObject *DivisionUndefined; /* also inherits ZeroDivisionError */
+static PyObject *InvalidContext;
+static PyObject *DivisionByZero; /* also inherits ZeroDivisionError */
+static PyObject *Inexact;
+static PyObject *Rounded;
+static PyObject *Overflow; /* also inherits Inexact */
+static PyObject *Underflow; /* also inherits Inexact, Subnormal */
+static PyObject *Subnormal;
+
+static PyObject *errors[NUMSIGNALS+NUMCONDITIONS];
+
+#define MAKE_METHODDEF(name) \
+ static PyMethodDef ml_##name = {"handle", (PyCFunction)handle_##name, \
+ METH_VARARGS};
+
+/* These are the "handle" methods for the individual exception classes.
+ * They are attached in init_decimal. */
+
+static PyObject *
+handle_DecimalException(PyObject *self, PyObject *args)
+{
+ /* XXX: implement */
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(DecimalException)
+
+static PyObject *
+handle_InvalidOperation(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(InvalidOperation)
+
+static PyObject *
+handle_ConversionSyntax(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(ConversionSyntax)
+
+static PyObject *
+handle_DivisionByZero(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(DivisionByZero)
+
+static PyObject *
+handle_DivisionImpossible(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(DivisionImpossible)
+
+static PyObject *
+handle_DivisionUndefined(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(DivisionUndefined)
+
+static PyObject *
+handle_InvalidContext(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(InvalidContext)
+
+static PyObject *
+handle_Overflow(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+MAKE_METHODDEF(Overflow)
+
+/* Forwarding ****************************************************************/
+
+static contextobject *getcontext(void);
+static int setcontext(contextobject *);
+static decimalobject *context_raise_error(contextobject *, int, char *, PyObject *);
+static PyObject *context_new(PyTypeObject *, PyObject *, PyObject *);
+static int decimal_nonzero(decimalobject *);
+static decimalobject *decimal_copy(decimalobject *);
+
+/* Decimal methods ***********************************************************/
+
+
+static decimalobject *
+_new_decimalobj(Py_ssize_t ndigits, char sign, long exp)
+{
+ decimalobject *new;
+ if (ndigits > PY_SSIZE_T_MAX) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ new = (decimalobject *)PyObject_NEW_VAR(decimalobject, &PyDecimal_DecimalType, ndigits);
+ if (new == NULL)
+ return NULL;
+ new->sign = sign;
+ new->exp = exp;
+ return new;
+}
+
+/* Check whether the number(s) aren't really numbers.
+ *
+ * If x and/or y are sNaN, signal, possibly storing the result
+ * of handle() in res (and return 1)
+ * If x and/or y are NaN, store NaN in res (and return 1)
+ * else return 0.
+ * On an unexpected error, return -1.
+ */
+static int
+_check_nans(decimalobject *x, decimalobject *y, contextobject *ctx, decimalobject **res)
+{
+ int nan1 = GETNAN(x);
+ int nan2 = 0;
+
+ if (y)
+ nan2 = GETNAN(y);
+
+ if (nan1 || nan2) {
+ PyObject *tup;
+
+ if (nan1 == 2) {
+ tup = Py_BuildValue("iO", 1, x);
+ if (!tup)
+ return -1;
+ *res = context_raise_error(ctx, S_INV_OPERATION, "sNaN", tup);
+ if (*res == NULL)
+ return -1;
+ return 1;
+ } else if (nan2 == 2) {
+ tup = Py_BuildValue("iO", 1, y);
+ if (!tup)
+ return -1;
+ *res = context_raise_error(ctx, S_INV_OPERATION, "sNaN", tup);
+ if (*res == NULL)
+ return -1;
+ return 1;
+ }
+
+ if (nan1)
+ *res = x;
+ else
+ *res = y;
+ /* since context_raise_error above returns new references, to be
+ * consistent we must incref the returns here too. */
+ Py_INCREF(*res);
+ return 1;
+ }
+ return 0;
+}
+
+/* default: rounding=-1 (use context), see the ROUND_* constants */
+static decimalobject *
+_decimal_round(decimalobject *self, long prec, contextobject *ctx, int rounding)
+{
+ /* XXX */
+ Py_RETURN_NONE;
+}
+
+/* Default values: rounding=-1 (use context), watchexp=1 */
+static decimalobject *
+_decimal_rescale(decimalobject *self, long exp, contextobject *ctx,
+ int rounding, int watchexp)
+{
+ decimalobject *ans = NULL;
+ int ret;
+ long diff;
+ Py_ssize_t digits, i;
+
+ if (ISSPECIAL(self)) {
+ if (ISINF(self))
+ return context_raise_error(ctx, S_INV_OPERATION, "rescale with an INF", NULL);
+ ret = _check_nans(self, NULL, ctx, &ans);
+ if (ret != 0)
+ /* ans is NULL in case of an error */
+ return ans;
+ }
+
+ if (watchexp && (exp > ctx->Emax || exp < ETINY(ctx)))
+ return context_raise_error(ctx, S_INV_OPERATION, "rescale(a, INF)", NULL);
+
+ if (!decimal_nonzero(self)) {
+ ans = _new_decimalobj(1, self->sign, exp);
+ if (!ans)
+ return NULL;
+ ans->digits[0] = 0;
+ return ans;
+ }
+
+ diff = self->exp - exp;
+ digits = self->ob_size + diff;
+
+ if (watchexp && ctx->prec < digits)
+ return context_raise_error(ctx, S_INV_OPERATION, "Rescale > prec", NULL);
+
+ digits += 1;
+ ans = _new_decimalobj(self->ob_size+1, self->sign, self->exp);
+ if (!ans)
+ return NULL;
+ for (i = 0; i < self->ob_size; i++)
+ ans->digits[i+1] = self->digits[i];
+ ans->digits[0] = 0;
+
+ if (digits < 0) {
+ ans->exp = -digits + ans->exp;
+ /* XXX: not complete... */
+ }
+
+
+
+}
+
+/* Fix the exponents and return a copy with the exponents in bounds.
+ * Only call if known not to be a special value. */
+static decimalobject *
+_fixexponents(decimalobject *self, contextobject *ctx)
+{
+ decimalobject *ans = NULL, *tmp;
+ PyObject *tup;
+ long adj;
+ assert(!ISSPECIAL(self));
+
+ adj = ADJUSTED(self);
+ if (adj < ctx->Emin) {
+ long Etiny = ETINY(ctx);
+ if (self->exp < Etiny) {
+ if (!decimal_nonzero(self)) {
+ ans = decimal_copy(self);
+ if (!ans)
+ return NULL;
+ ans->exp = Etiny;
+ tmp = context_raise_error(ctx, S_CLAMPED, NULL, NULL);
+ if (!tmp)
+ goto err;
+ Py_DECREF(tmp);
+ return ans;
+ }
+ ans = _decimal_rescale(self, Etiny, ctx, -1, 1);
+ if (!ans)
+ return NULL;
+ tmp = context_raise_error(ctx, S_SUBNORMAL, NULL, NULL);
+ if (!tmp)
+ goto err;
+ Py_DECREF(tmp);
+ if (ISFLAGSET(ctx->flags, S_INEXACT)) {
+ tmp = context_raise_error(ctx, S_UNDERFLOW, NULL, NULL);
+ if (!tmp)
+ goto err;
+ Py_DECREF(tmp);
+ }
+ return ans;
+ } else {
+ if (decimal_nonzero(self)) {
+ tmp = context_raise_error(ctx, S_SUBNORMAL, NULL, NULL);
+ if (!tmp)
+ return NULL;
+ Py_DECREF(tmp);
+ }
+ /* this returns self, below */
+ }
+ } else {
+ long Etop = ETOP(ctx);
+ if (ctx->clamp && self->exp > Etop) {
+ tmp = context_raise_error(ctx, S_CLAMPED, NULL, NULL);
+ if (!tmp)
+ return NULL;
+ Py_DECREF(tmp);
+ ans = _decimal_rescale(self, Etop, ctx, -1, 1);
+ if (!ans)
+ return NULL;
+ return ans;
+ } else {
+ if (adj > ctx->Emax) {
+ if (!decimal_nonzero(self)) {
+ ans = decimal_copy(self);
+ if (!ans)
+ return NULL;
+ ans->exp = ctx->Emax;
+ tmp = context_raise_error(ctx, S_CLAMPED, NULL, NULL);
+ if (!tmp)
+ goto err;
+ Py_DECREF(tmp);
+ return ans;
+ }
+ tmp = context_raise_error(ctx, S_INEXACT, NULL, NULL);
+ if (!tmp)
+ return NULL;
+ Py_DECREF(tmp);
+ tmp = context_raise_error(ctx, S_ROUNDED, NULL, NULL);
+ if (!tmp)
+ return NULL;
+ Py_DECREF(tmp);
+ tup = Py_BuildValue("(b)", self->sign);
+ if (!tup)
+ return NULL;
+ ans = context_raise_error(ctx, S_OVERFLOW, "above Emax", tup);
+ Py_DECREF(tup);
+ return ans;
+ }
+ }
+ }
+
+ Py_INCREF(self);
+ return self;
+err:
+ Py_XDECREF(ans);
+ return NULL;
+}
+
+static decimalobject *
+_decimal_fix(decimalobject *self, contextobject *ctx)
+{
+ decimalobject *ans = NULL;
+
+ if (ISSPECIAL(self)) {
+ Py_INCREF(self);
+ return self;
+ }
+
+ ans = _fixexponents(self, ctx);
+ if (!ans)
+ return NULL;
+
+ if (ans->ob_size > ctx->prec) {
+ decimalobject *rounded;
+ rounded = _decimal_round(ans, ctx->prec, ctx, -1);
+ Py_DECREF(ans);
+ if (!rounded)
+ return NULL;
+ ans = _fixexponents(self, ctx);
+ Py_DECREF(rounded);
+ if (!ans)
+ return NULL;
+ }
+ return ans;
+}
+
+#define STUB_HEAD static PyObject *
+#define STUB_TAIL (PyObject *self, PyObject *args) { return NULL; }
+#define STUB_TAIL1 (PyObject *self) { return NULL; }
+#define STUB(name) STUB_HEAD decimal_##name STUB_TAIL
+#define STUB1(name) STUB_HEAD decimal_##name STUB_TAIL1
+
+static char *ctxkwlist[] = {"context", 0};
+
+#define PARSECONTEXT(methname) \
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:" methname, ctxkwlist, &ctx)) \
+ { return NULL; } \
+ if (ctx == NULL) { \
+ if (!(ctx = getcontext())) return NULL; \
+ }
+
+STUB(compare)
+STUB(max)
+STUB(min)
+
+static PyObject *
+decimal_normalize(decimalobject *self, PyObject *args, PyObject *kwds)
+{
+ decimalobject *dup;
+ contextobject *ctx = NULL;
+ PARSECONTEXT("normalize");
+
+ if (ISSPECIAL(self)) {
+ decimalobject *nan = NULL;
+ int res;
+ res = _check_nans(self, NULL, ctx, &nan);
+ /* if error set or NaN returned, return */
+ if (res != 0) return (PyObject *)nan;
+ }
+
+ dup = _decimal_fix(self, ctx);
+
+ /* XXX: incomplete */
+}
+
+STUB(quantize)
+STUB(remainder_near)
+STUB(same_quantum)
+STUB(sqrt)
+STUB(to_eng_string)
+STUB(to_integral)
+STUB(reduce)
+STUB(deepcopy)
+
+static decimalobject *
+decimal_copy(decimalobject *self)
+{
+ decimalobject *new;
+ Py_ssize_t size = self->ob_size;
+
+ new = _new_decimalobj(size, self->sign, self->exp);
+ if (!new)
+ return NULL;
+ while (--size)
+ new->digits[size] = self->digits[size];
+ return new;
+}
+
+static PyObject *
+decimal_adjusted(decimalobject *self)
+{
+ if (ISSPECIAL(self))
+ return PyInt_FromLong(0);
+
+ /* XXX: Overflow? */
+ return PyInt_FromSsize_t(ADJUSTED(self));
+}
+
+static PyObject *
+decimal_as_tuple(decimalobject *self)
+{
+ PyObject *digits, *res, *d;
+ Py_ssize_t i;
+
+ digits = PyTuple_New(self->ob_size);
+ if (!digits)
+ return NULL;
+
+ for (i = 0; i < self->ob_size; i++) {
+ d = PyInt_FromLong(self->digits[i]);
+ if (!d) return NULL;
+ PyTuple_SET_ITEM(digits, i, d);
+ }
+
+ res = Py_BuildValue("(bOn)", self->sign % 2, digits, self->exp);
+ Py_DECREF(digits);
+ return res;
+}
+
+static PyMethodDef decimal_methods[] = {
+ {"adjusted", (PyCFunction)decimal_adjusted,
+ METH_NOARGS,
+ PyDoc_STR("Return the adjusted exponent of self.")},
+ {"as_tuple", (PyCFunction)decimal_as_tuple,
+ METH_NOARGS,
+ PyDoc_STR("Represents the number as a triple tuple.\n\n"
+ "To show the internals exactly as they are.")},
+ {"compare", (PyCFunction)decimal_compare,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Compares one to another.\n\n -1 => a < b\n"
+ " 0 => a = b\n 1 => a > b\n"
+ " NaN => one is NaN\n"
+ "Like __cmp__, but returns Decimal instances.")},
+ {"max", (PyCFunction)decimal_max,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Returns the larger value.\n\n"
+ "like max(self, other) except if one is not a number, \n"
+ "returns NaN (and signals if one is sNaN). Also rounds.")},
+ {"min", (PyCFunction)decimal_min,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Returns the smaller value.\n\n"
+ "like min(self, other) except if one is not a number, \n"
+ "returns NaN (and signals if one is sNaN). Also rounds.")},
+ {"normalize", (PyCFunction)decimal_normalize,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Normalize- strip trailing 0s, change anything equal"
+ "to 0 to 0e0")},
+ {"quantize", (PyCFunction)decimal_quantize,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Quantize self so its exponent is the same as that of exp.\n\n"
+ "Similar to self._rescale(exp._exp) but with error checking.")},
+ {"remainder_near", (PyCFunction)decimal_remainder_near,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Remainder nearest to 0- abs(remainder-near) <= other/2")},
+ {"same_quantum", (PyCFunction)decimal_same_quantum,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Test whether self and other have the same exponent.\n\n"
+ "same as self._exp == other._exp, except NaN == sNaN")},
+ {"sqrt", (PyCFunction)decimal_sqrt,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Return the square root of self.\n\n"
+ "Uses a converging algorithm (Xn+1 = 0.5*(Xn + self / Xn))\n"
+ "Should quadratically approach the right answer.")},
+ {"to_eng_string", (PyCFunction)decimal_to_eng_string,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Convert to engineering-type string.\n\n"
+ "Engineering notation has an exponent which is a multiple "
+ "of 3, so there\nare up to 3 digits left of the decimal "
+ "place.\n\nSame rules for when in exponential and when as "
+ "a value as in __str__.")},
+ {"to_integral", (PyCFunction)decimal_to_integral,
+ METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("Rounds to the nearest integer, without raising "
+ "inexact, rounded.")},
+ {"__reduce__", (PyCFunction)decimal_reduce,
+ METH_NOARGS},
+ {"__copy__", (PyCFunction)decimal_copy,
+ METH_NOARGS},
+ {"__deepcopy__", decimal_deepcopy,
+ METH_O},
+ {NULL, NULL}
+};
+
+static char decimal_doc[] =
+PyDoc_STR("Floating point class for decimal arithmetic.");
+
+STUB(add)
+STUB(subtract)
+STUB(multiply)
+STUB(divide)
+STUB(remainder)
+STUB(divmod)
+STUB(power)
+STUB1(negative)
+STUB1(positive)
+STUB1(absolute)
+STUB1(invert)
+STUB1(int)
+STUB1(long)
+STUB1(float)
+STUB(floor_div)
+STUB(true_div)
+
+static int
+decimal_nonzero(decimalobject *self)
+{
+ /* XXX */
+ return 0;
+}
+
+static PyNumberMethods decimal_as_number = {
+ decimal_add, /* nb_add */
+ decimal_subtract, /* nb_subtract */
+ decimal_multiply, /* nb_multiply */
+ decimal_divide, /* nb_divide */
+ decimal_remainder, /* nb_remainder */
+ decimal_divmod, /* nb_divmod */
+ decimal_power, /* nb_power */
+ decimal_negative, /* nb_negative */
+ decimal_positive, /* nb_positive */
+ decimal_absolute, /* nb_absolute */
+ decimal_nonzero, /* nb_nonzero */
+ decimal_invert, /* nb_invert */
+ 0, /* nb_lshift */
+ 0, /* nb_rshift */
+ 0, /* nb_and */
+ 0, /* nb_xor */
+ 0, /* nb_or */
+ 0, /* nb_coerce */
+ decimal_int, /* nb_int */
+ decimal_long, /* nb_long */
+ decimal_float, /* nb_float */
+ 0, /* nb_oct */
+ 0, /* nb_hex */
+ 0, /* nb_inplace_add */
+ 0, /* nb_inplace_subtract */
+ 0, /* nb_inplace_multiply */
+ 0, /* nb_inplace_divide */
+ 0, /* nb_inplace_remainder */
+ 0, /* nb_inplace_power */
+ 0, /* nb_inplace_lshift */
+ 0, /* nb_inplace_rshift */
+ 0, /* nb_inplace_and */
+ 0, /* nb_inplace_xor */
+ 0, /* nb_inplace_or */
+ decimal_floor_div, /* nb_floor_divide */
+ decimal_true_div, /* nb_true_divide */
+ 0, /* nb_inplace_floor_divide */
+ 0, /* nb_inplace_true_divide */
+ 0, /* nb_index */
+
+};
+
+
+static PyObject *
+decimal_richcompare(decimalobject *a, decimalobject *b, int op)
+{
+ /* XXX */
+ return NULL;
+}
+
+int
+_decimal_isint(decimalobject *d)
+{
+ Py_ssize_t i;
+
+ if (d->exp >= 0)
+ /* if the exponent is positive, there's no
+ * fractional part at all. */
+ return 1;
+ /* Here comes the tricky part. We have to find out
+ * whether the fractional part consists only of zeroes. */
+ for (i = d->ob_size-1; i > d->ob_size + d->exp; --i) {
+ if (d->digits[i] > 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+decimal_dealloc(PyObject *d)
+{
+ d->ob_type->tp_free(d);
+}
+
+static decimalobject *
+_decimal_fromliteralnan(char *str, Py_ssize_t len, contextobject *ctx)
+{
+ char literalsign = 0, sign = 0;
+ decimalobject *new;
+ Py_ssize_t size = 0, i;
+ char *start, *p;
+
+ if (len < 3)
+ return NULL;
+ /* remove sign */
+ if (str[0] == '+')
+ str++;
+ else if (str[0] == '-') {
+ str++;
+ literalsign = 1;
+ }
+ if (tolower(str[0]) == 'n' &&
+ tolower(str[1]) == 'a' &&
+ tolower(str[2]) == 'n') {
+
+ sign = (literalsign ? SIGN_NEGNAN : SIGN_POSNAN);
+ if (len < 4) goto finish;
+ p = str+3;
+ } else if (tolower(str[0]) == 's' &&
+ tolower(str[1]) == 'n' &&
+ tolower(str[2]) == 'a' &&
+ tolower(str[3]) == 'n') {
+ sign = (literalsign ? SIGN_NEGSNAN : SIGN_POSSNAN);
+ if (len < 5) goto finish;
+ p = str+4;
+ } else {
+ return NULL;
+ }
+ /* diagnostic info: skip leading zeroes */
+ while (*p == '0')
+ p++;
+ start = p;
+ while (isdigit(*p++))
+ size++;
+ if (p == str+len+1)
+ goto finish;
+ return NULL;
+
+finish:
+ if (size > ctx->prec)
+ return context_raise_error(ctx, C_CONV_SYNTAX,
+ "diagnostic info too long", NULL);
+
+ new = _new_decimalobj(size, sign, 0);
+ if (!new)
+ return NULL;
+ for (i = 0; i < size; i++)
+ new->digits[i] = *(start+i) -48;
+ return new;
+}
+
+static char *infinities[] = {
+ "inf", "infinity", "+inf", "+infinity",
+ "-inf", "-infinity", 0
+};
+
+static decimalobject *
+_decimal_fromliteralinfinity(char *str)
+{
+ decimalobject *new;
+ char **p = infinities;
+ char sign = 0;
+ do {
+ if (strcasecmp(str, *p) == 0) {
+ if (str[0] == '-')
+ sign = SIGN_NEGINF;
+ else
+ sign = SIGN_POSINF;
+ break;
+ }
+ } while (*++p);
+ if (sign) {
+ new = _new_decimalobj(1, sign, 0);
+ if (!new)
+ return NULL;
+ new->digits[0] = 0;
+ return new;
+ }
+ return NULL;
+}
+
+static decimalobject *
+_decimal_fromliteral(char *str, Py_ssize_t len, contextobject *ctx)
+{
+ Py_ssize_t ipos = 0; /* start of integral digits */
+ Py_ssize_t dpos = -1; /* decimal point location */
+ Py_ssize_t dend = len; /* end of integral/decimal digits */
+ char *p = str;
+ char sign = 0;
+ decimalobject *new;
+ long exp = 0;
+ Py_ssize_t i = 0, ndigits;
+
+ /* optional sign */
+ if (*p == '+') {
+ ++p;
+ ipos = 1;
+ } else if (*p == '-') {
+ ++p;
+ ipos = 1;
+ sign = 1;
+ }
+
+ do {
+ if (*p == '.') {
+ dpos = p-str;
+ } else if (*p == 'e' || *p == 'E') {
+ dend = p-str;
+ if (dend == ipos || (dpos >= 0 && dend == ipos+1))
+ /* no digits between sign and E */
+ goto err;
+ ++p;
+ break;
+ } else if (!isdigit(*p)) {
+ goto err;
+ }
+ } while (*++p);
+
+ /* exponential part or end of string */
+ if (*p == 0) {
+ if (ipos == dpos && dpos == dend-1)
+ /* no digits at all */
+ goto err;
+ goto finish;
+ } else if (*p == '-' || *p == '+') {
+ ++p;
+ }
+
+ do {
+ /* optional exponential digits */
+ if (!isdigit(*p))
+ goto err;
+ } while (*++p);
+
+finish:
+
+ if (dend < len)
+ /* XXX: which function to convert a string to ssize_t? */
+ exp = atol(str + dend + 1);
+ if (dpos >= 0)
+ /* specified fractional digits, reduce exp */
+ exp -= dend - dpos - 1;
+
+ ndigits = dend - ipos - (dpos<0 ? 0 : 1);
+ new = _new_decimalobj(ndigits, sign, exp);
+ if (!new)
+ return NULL;
+ /* XXX: leading zeroes are not stripped */
+ for (p = str+ipos; p-str < dend; ++p) {
+ if (*p == '.') continue;
+ new->digits[i] = *p - 48;
+ i++;
+ }
+ return new;
+
+err:
+ return context_raise_error(ctx, C_CONV_SYNTAX,
+ "invalid literal for Decimal", NULL);
+}
+
+PyObject *
+PyDecimal_FromString(char *buffer, Py_ssize_t buf_len, contextobject *ctx)
+{
+ decimalobject *new;
+
+ new = _decimal_fromliteralinfinity(buffer);
+ if (new) return (PyObject *)new;
+ if (PyErr_Occurred()) return NULL;
+
+ new = _decimal_fromliteralnan(buffer, buf_len, ctx);
+ if (new) return (PyObject *)new;
+ if (PyErr_Occurred()) return NULL;
+
+ return (PyObject *)_decimal_fromliteral(buffer, buf_len, ctx);
+}
+
+
+PyObject *
+PyDecimal_FromLong(long value)
+{
+ decimalobject *new;
+ long v = value;
+ int ndigits = 0, neg = 0, i = 0;
+
+ if (value == 0) {
+ new = _new_decimalobj(1, 0, 0);
+ if (!new) return NULL;
+ new->digits[0] = 0;
+ return (PyObject *)new;
+ } else if (value < 0) {
+ v = -value;
+ neg = 1;
+ }
+
+ while (v) {
+ ndigits++;
+ v /= 10;
+ }
+
+ new = _new_decimalobj(ndigits, (neg ? SIGN_NEG : SIGN_POS), 0);
+ if (!new) return NULL;
+ while (value) {
+ new->digits[ndigits-i-1] = value % 10;
+ value /= 10;
+ i++;
+ }
+ return (PyObject *)new;
+}
+
+/* convert from a 3-tuple of (sign, digits, exp) */
+PyObject *
+PyDecimal_FromSequence(PyObject *seq)
+{
+ decimalobject *new;
+ PyObject *tup, *digits, *digtup = NULL, *item;
+ int sign;
+ long exp;
+ Py_ssize_t i;
+
+ if (PyTuple_Check(seq)) {
+ tup = seq;
+ Py_INCREF(tup);
+ } else {
+ tup = PySequence_Tuple(seq);
+ if (!tup)
+ return NULL;
+ }
+
+ if (PyTuple_GET_SIZE(tup) != 3) {
+ PyErr_SetString(PyExc_ValueError, "Invalid arguments");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(tup, "iOl", &sign, &digits, &exp))
+ return NULL;
+ if (sign < 0 || sign > 7) {
+ PyErr_SetString(PyExc_ValueError, "Invalid sign");
+ return NULL;
+ }
+ digtup = PySequence_Tuple(digits);
+ if (!digtup) {
+ PyErr_SetString(PyExc_ValueError, "Invalid arguments");
+ goto err;
+ }
+ new = _new_decimalobj(PyTuple_GET_SIZE(digtup), sign, exp);
+ for (i = 0; i < new->ob_size; i++) {
+ item = PyTuple_GET_ITEM(digtup, i);
+ if (PyInt_Check(item)) {
+ long x = PyInt_AsLong(item);
+ if (x < 0 || x > 9) {
+ PyErr_Format(PyExc_ValueError, "Invalid digit: %ld", x);
+ return NULL;
+ }
+ new->digits[i] = (signed char)x;
+ } else if (PyLong_Check(item)) {
+ long x = PyLong_AsLong(item);
+ if (x == -1 && PyErr_Occurred())
+ return NULL;
+ if (x < 0 || x > 9) {
+ PyErr_Format(PyExc_ValueError, "Invalid digit: %ld", x);
+ return NULL;
+ }
+ new->digits[i] = (signed char)x;
+ } else {
+ PyErr_SetString(PyExc_ValueError, "The second value in the tuple "
+ "must be composed of non negative integer elements.");
+ return NULL;
+ }
+ }
+
+ Py_DECREF(digtup);
+ Py_DECREF(tup);
+ return (PyObject *)new;
+
+err:
+ Py_XDECREF(digtup);
+ Py_DECREF(tup);
+ Py_DECREF(new);
+ return NULL;
+}
+
+static PyObject *
+decimal_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"value", "context", 0};
+ PyObject *value = NULL;
+ PyObject *context = NULL;
+ PyObject *repr;
+ contextobject *ctx;
+ int decref_value = 0;
+ char *buffer;
+ Py_ssize_t buf_len = 0;
+
+ /* XXX
+ if (type != &PyDecimal_DecimalType)
+ return decimal_subtype_new(type, args, kwds);
+ */
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:Decimal",
+ kwlist, &value, &context))
+ return NULL;
+ if (value == NULL) {
+ value = PyString_FromString("0");
+ decref_value = 1;
+ }
+
+ if (PyFloat_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "Cannot convert float to Decimal. "
+ "First convert the float to a string.");
+ return NULL;
+ }
+
+ if (PyDecimal_Check(value))
+ return (PyObject *)decimal_copy((decimalobject *)value);
+
+ if (PyList_Check(value) || PyTuple_Check(value))
+ return PyDecimal_FromSequence(value);
+
+ if (PyInt_Check(value)) {
+ long x = PyInt_AsLong(value);
+ if (x == -1 && PyErr_Occurred())
+ return NULL;
+ return PyDecimal_FromLong(x);
+ }
+
+ if (PyLong_Check(value)) {
+ /* XXX: possibly do div-mod-loop */
+ value = PyObject_Str(value);
+ if (!value)
+ return NULL;
+ decref_value = 1;
+ }
+
+ ctx = getcontext();
+ if (!ctx) {
+ if (decref_value) {
+ Py_DECREF(value);
+ }
+ return NULL;
+ }
+
+ /* try buffer interface (e.g. strings and unicode) */
+ if (PyObject_AsCharBuffer(value, (const char **)&buffer, &buf_len) == 0) {
+ PyObject *res = (PyObject *)PyDecimal_FromString(buffer, buf_len, ctx);
+ if (decref_value) {
+ Py_DECREF(value);
+ }
+ return res;
+ }
+
+ /* if PyObject_AsCharBuffer didn't set a TypeError, fail immediately */
+ if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError))
+ return NULL;
+
+ /* no suitable type found */
+ repr = PyObject_Repr(value);
+ if (!repr)
+ return NULL;
+ if (!PyString_Check(repr))
+ PyErr_SetString(PyExc_TypeError, "Cannot convert object to Decimal");
+ else
+ PyErr_Format(PyExc_TypeError, "Cannot convert %s to Decimal",
+ PyString_AS_STRING(repr));
+ Py_DECREF(repr);
+ return NULL;
+}
+
+static PyObject *
+decimal_str(decimalobject *d)
+{
+ /* XXX: quick hack */
+ char buf[1000];
+ char dig[2];
+ Py_ssize_t i;
+
+ dig[1] = 0;
+ PyOS_snprintf(buf, 1000, "sign=%i, exp=%ld, digits=", d->sign, d->exp);
+ for (i = 0; i < d->ob_size; i++) {
+ dig[0] = d->digits[i]+48;
+ strcat(buf, dig);
+ }
+ return PyString_FromString(buf);
+}
+
+static PyObject *
+decimal_repr(decimalobject *d)
+{
+ PyObject *str, *res;
+ static PyObject *format;
+
+ if (!format) {
+ format = PyString_FromString("Decimal(\"%s\")");
+ }
+ str = decimal_str(d);
+ if (!str)
+ return NULL;
+ res = PyString_Format(format, str);
+ Py_DECREF(str);
+ return res;
+}
+
+static long
+decimal_hash(decimalobject *d)
+{
+ PyObject *tmp, *tmp2;
+ long hash;
+
+ if (ISSPECIAL(d)) {
+ if (GETNAN(d)) {
+ PyErr_SetString(PyExc_TypeError, "Cannot hash a NaN value.");
+ return -1;
+ }
+ tmp = decimal_str(d);
+ if (!tmp)
+ return -1;
+ hash = PyObject_Hash(tmp);
+ Py_DECREF(tmp);
+ return hash;
+ }
+ if (_decimal_isint(d)) {
+ tmp = decimal_int(d);
+ if (!tmp)
+ return -1;
+ hash = PyObject_Hash(tmp);
+ Py_DECREF(tmp);
+ return hash;
+ }
+ tmp2 = PyTuple_New(0);
+ if (!tmp2)
+ return -1;
+ tmp = decimal_normalize(d, tmp2, NULL);
+ Py_DECREF(tmp2);
+ if (!tmp)
+ return -1;
+ tmp2 = decimal_str((decimalobject *)tmp);
+ Py_DECREF(tmp);
+ if (!tmp2)
+ return -1;
+ hash = PyObject_Hash(tmp2);
+ Py_DECREF(tmp2);
+ return hash;
+}
+
+
+static PyTypeObject PyDecimal_DecimalType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ MODULE_NAME ".Decimal", /* tp_name */
+ sizeof(decimalobject) - sizeof(char), /* tp_basicsize */
+ sizeof(char), /* tp_itemsize */
+ (destructor)decimal_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)decimal_repr, /* tp_repr */
+ &decimal_as_number, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)decimal_hash, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)decimal_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
+ decimal_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ (richcmpfunc)decimal_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ decimal_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ decimal_new, /* tp_new */
+ PyObject_Del, /* tp_free */
+};
+
+
+/* Context threading magic ***************************************************/
+
+#ifdef WITH_THREAD
+
+static contextobject *
+getcontext(void)
+{
+ PyObject *tdict;
+ contextobject *ctx;
+
+ tdict = PyThreadState_GetDict();
+ if (!tdict) {
+ PyErr_SetString(PyExc_SystemError,
+ "Couldn't get thread-state dictionary");
+ return NULL;
+ }
+ ctx = (contextobject *)PyDict_GetItemString(tdict, MODULE_NAME ".context");
+ if (!ctx) {
+ PyObject *args;
+ args = PyTuple_New(0);
+ if (!args)
+ return NULL;
+ ctx = (contextobject *)context_new(&PyDecimal_DecimalContextType, args, NULL);
+ Py_DECREF(args);
+ if (!ctx)
+ return NULL;
+ if (PyDict_SetItemString(tdict, MODULE_NAME ".context",
+ (PyObject *)ctx) < 0) {
+ Py_DECREF(ctx);
+ return NULL;
+ }
+ /* clear the KeyError */
+ PyErr_Clear();
+ return ctx;
+ }
+ Py_INCREF(ctx);
+ return ctx;
+}
+
+static int
+setcontext(contextobject *ctx)
+{
+ PyObject *tdict;
+
+ tdict = PyThreadState_GetDict();
+ if (!tdict) {
+ PyErr_SetString(PyExc_SystemError,
+ "Couldn't get thread-state dictionary");
+ return -1;
+ }
+ if (PyDict_SetItemString(tdict, MODULE_NAME ".context",
+ (PyObject *)ctx) < 0)
+ return -1;
+ return 0;
+}
+
+#else
+
+static PyObject *current_context;
+
+static contextobject *
+getcontext()
+{
+ if (current_context == NULL) {
+ PyObject *args;
+ args = PyTuple_New(0);
+ if (!args)
+ return NULL;
+ current_context = context_new(&PyDecimal_DecimalContextType, args, NULL);
+ Py_DECREF(args);
+ if (!current_context)
+ return NULL;
+ }
+ return (contextobject *)current_context;
+}
+
+static int
+setcontext(contextobject *ctx)
+{
+ Py_CLEAR(current_context);
+ Py_INCREF(ctx);
+ current_context = ctx;
+ return 0;
+}
+
+#endif /* WITH_THREAD */
+
+/* Context object ************************************************************/
+
+static contextobject *
+_new_contextobj(long prec, int rounding, int rounding_dec, int traps,
+ int flags, long Emin, long Emax, int capitals,
+ int clamp, int ignored)
+{
+ contextobject *new;
+ new = (contextobject *)PyObject_NEW(contextobject, &PyDecimal_DecimalContextType);
+ if (new == NULL)
+ return NULL;
+ new->prec = prec;
+ new->rounding = rounding;
+ new->rounding_dec = rounding_dec;
+ new->Emin = Emin;
+ new->Emax = Emax;
+ new->capitals = capitals;
+ new->clamp = clamp;
+ new->flags = flags;
+ new->traps = traps;
+ new->ignored = ignored;
+ return new;
+}
+
+/* Context methods ***********************************************************/
+
+static decimalobject *
+context_raise_error(contextobject *self, int cond, char *explanation, PyObject *args)
+{
+ int err = cond;
+ PyObject *exc, *res, *func;
+
+ /* map subclasses of InvalidOperation to InvalidOperation */
+ if (cond >= NUMSIGNALS)
+ err = S_INV_OPERATION;
+
+ if (ISFLAGSET(self->ignored, err)) {
+ exc = PyObject_CallObject(errors[err], NULL);
+ if (!exc)
+ return NULL;
+ func = PyObject_GetAttrString(exc, "handle");
+ if (!func) {
+ Py_DECREF(exc);
+ return NULL;
+ }
+ res = PyObject_CallObject(func, args);
+ Py_DECREF(func);
+ Py_DECREF(exc);
+ if (res == NULL)
+ return NULL;
+ return (decimalobject *)res;
+ }
+
+ SETFLAG(self->flags, err);
+ if (!ISFLAGSET(self->traps, err)) {
+ /* Let the exception class decide how to handle the condition. */
+ exc = PyObject_CallObject(errors[cond], NULL);
+ if (!exc)
+ return NULL;
+ func = PyObject_GetAttrString(exc, "handle");
+ if (!func) {
+ Py_DECREF(exc);
+ return NULL;
+ }
+ res = PyObject_CallObject(func, args);
+ Py_DECREF(func);
+ Py_DECREF(exc);
+ if (res == NULL)
+ return NULL;
+ return (decimalobject *)res;
+ }
+
+ PyErr_SetString(errors[err], (explanation ? explanation : ""));
+ return NULL;
+}
+
+#define CSTUB(name) STUB_HEAD context_##name STUB_TAIL
+
+static PyObject *
+context_clear_flags(contextobject *self)
+{
+ self->flags = 0;
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+context_copy(contextobject *self)
+{
+ return NULL;
+}
+
+CSTUB(create_decimal)
+
+static PyObject *
+context_Etiny(contextobject *self)
+{
+ return PyInt_FromSsize_t(ETINY(self));
+}
+
+static PyObject *
+context_Etop(contextobject *self)
+{
+ return PyInt_FromSsize_t(ETOP(self));
+}
+
+CSTUB(abs)
+CSTUB(add)
+CSTUB(compare)
+CSTUB(divide)
+CSTUB(divmod)
+CSTUB(max)
+CSTUB(min)
+CSTUB(minus)
+CSTUB(multiply)
+CSTUB(normalize)
+CSTUB(plus)
+CSTUB(power)
+CSTUB(quantize)
+CSTUB(remainder)
+CSTUB(remainder_near)
+CSTUB(same_quantum)
+CSTUB(sqrt)
+CSTUB(subtract)
+CSTUB(to_eng_string)
+CSTUB(to_integral)
+CSTUB(to_sci_string)
+
+
+static PyMethodDef context_methods[] = {
+ {"clear_flags", (PyCFunction)context_clear_flags,
+ METH_NOARGS},
+ {"copy", (PyCFunction)context_copy,
+ METH_NOARGS},
+ {"create_decimal", (PyCFunction)context_create_decimal,
+ METH_VARARGS | METH_KEYWORDS},
+ {"Etiny", (PyCFunction)context_Etiny,
+ METH_NOARGS},
+ {"Etop", (PyCFunction)context_Etop,
+ METH_NOARGS},
+ {"abs", (PyCFunction)context_abs,
+ METH_VARARGS | METH_KEYWORDS},
+ {"add", (PyCFunction)context_add,
+ METH_VARARGS | METH_KEYWORDS},
+ {"compare", (PyCFunction)context_compare,
+ METH_VARARGS | METH_KEYWORDS},
+ {"divide", (PyCFunction)context_divide,
+ METH_VARARGS | METH_KEYWORDS},
+ {"divmod", (PyCFunction)context_divmod,
+ METH_VARARGS | METH_KEYWORDS},
+ {"max", (PyCFunction)context_max,
+ METH_VARARGS | METH_KEYWORDS},
+ {"min", (PyCFunction)context_min,
+ METH_VARARGS | METH_KEYWORDS},
+ {"minus", (PyCFunction)context_minus,
+ METH_VARARGS | METH_KEYWORDS},
+ {"multiply", (PyCFunction)context_multiply,
+ METH_VARARGS | METH_KEYWORDS},
+ {"normalize", (PyCFunction)context_normalize,
+ METH_VARARGS | METH_KEYWORDS},
+ {"plus", (PyCFunction)context_plus,
+ METH_VARARGS | METH_KEYWORDS},
+ {"power", (PyCFunction)context_power,
+ METH_VARARGS | METH_KEYWORDS},
+ {"quantize", (PyCFunction)context_quantize,
+ METH_VARARGS | METH_KEYWORDS},
+ {"remainder", (PyCFunction)context_remainder,
+ METH_VARARGS | METH_KEYWORDS},
+ {"remainder_near", (PyCFunction)context_remainder_near,
+ METH_VARARGS | METH_KEYWORDS},
+ {"same_quantum", (PyCFunction)context_same_quantum,
+ METH_VARARGS | METH_KEYWORDS},
+ {"sqrt", (PyCFunction)context_sqrt,
+ METH_VARARGS | METH_KEYWORDS},
+ {"subtract", (PyCFunction)context_subtract,
+ METH_VARARGS | METH_KEYWORDS},
+ {"to_eng_string", (PyCFunction)context_to_eng_string,
+ METH_VARARGS | METH_KEYWORDS},
+ {"to_integral", (PyCFunction)context_to_integral,
+ METH_VARARGS | METH_KEYWORDS},
+ {"to_sci_string", (PyCFunction)context_to_sci_string,
+ METH_VARARGS | METH_KEYWORDS},
+ {"__copy__", (PyCFunction)context_copy,
+ METH_NOARGS},
+ {NULL, NULL}
+};
+
+static char context_doc[] =
+PyDoc_STR("Contains the context for a Decimal instance.");
+
+
+static void
+context_dealloc(PyObject *c)
+{
+ c->ob_type->tp_free(c);
+}
+
+CSTUB(richcompare)
+
+static PyObject *
+context_repr(contextobject *self)
+{
+ char roundstr[20] = "ROUND_";
+ char flags[1000] = "{}";
+ char traps[1000] = "[]";
+
+ switch (self->rounding) {
+ case ROUND_UP:
+ strcat(roundstr, "UP");
+ break;
+ case ROUND_DOWN:
+ strcat(roundstr, "DOWN");
+ break;
+ case ROUND_HALF_DOWN:
+ strcat(roundstr, "HALF_DOWN");
+ break;
+ case ROUND_HALF_EVEN:
+ strcat(roundstr, "HALF_EVEN");
+ break;
+ case ROUND_HALF_UP:
+ strcat(roundstr, "HALF_UP");
+ break;
+ case ROUND_FLOOR:
+ strcat(roundstr, "FLOOR");
+ break;
+ case ROUND_CEILING:
+ strcat(roundstr, "CEILING");
+ break;
+ default:
+ strcpy(roundstr, "None");
+ }
+
+ /* XXX: flags/traps */
+
+ return PyString_FromFormat("Context(prec=%ld, rounding=%s, Emin=%ld, "
+ "Emax=%ld, capitals=%d, flags=%s, traps=%s)",
+ self->prec, roundstr, self->Emin, self->Emax,
+ self->capitals, flags, traps);
+}
+
+static long
+context_hash(PyObject *c)
+{
+ PyErr_SetString(PyExc_TypeError, "Cannot hash a Context.");
+ return -1;
+}
+
+static PyObject *
+context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"prec", "rounding", "_rounding_decision",
+ "traps", "flags", "Emin", "Emax",
+ "capitals", "_clamp", "_ignored_flags", 0};
+ long prec = LONG_MIN;
+ long Emin = LONG_MAX, Emax = LONG_MIN;
+ int rounding = -1, rounding_dec = -1, capitals = -1, clamp = 0;
+ PyObject *pytraps = NULL, *pyflags = NULL, *pyignored = NULL;
+ PyObject *tmp;
+ int traps = 0, flags = 0, ignored = 0;
+ int i, j;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|nbbOOnnbbO:Context", kwlist,
+ &prec, &rounding, &rounding_dec, &pytraps,
+ &pyflags, &Emin, &Emax, &capitals, &clamp,
+ &pyignored))
+ return NULL;
+
+ if (pytraps == NULL) {
+ traps = PyDecimal_DefaultContext->traps;
+ } else if (!PyDict_Check(pytraps)) {
+ for (i = 0; i < NUMSIGNALS; i++) {
+ j = PySequence_Contains(pytraps, errors[i]);
+ if (j == -1)
+ return NULL;
+ else if (j == 1)
+ SETFLAG(traps, i);
+ }
+ } else {
+ for (i = 0; i < NUMSIGNALS; i++) {
+ j = PyDict_Contains(pytraps, errors[i]);
+ if (j == -1)
+ return NULL;
+ else if (j == 0)
+ continue;
+ tmp = PyDict_GetItem(pytraps, errors[i]);
+ if (!tmp)
+ return NULL;
+ j = PyObject_IsTrue(tmp);
+ if (j == -1)
+ return NULL;
+ else if (j == 1)
+ SETFLAG(traps, i);
+ }
+ }
+
+ if (pyflags == NULL) {
+ /* don't copy flags from default context */
+ } else if (PyDict_Check(pyflags)) {
+ for (i = 0; i < NUMSIGNALS; i++) {
+ j = PyDict_Contains(pyflags, errors[i]);
+ if (j == -1)
+ return NULL;
+ else if (j == 0)
+ continue;
+ tmp = PyDict_GetItem(pyflags, errors[i]);
+ if (!tmp)
+ return NULL;
+ j = PyObject_IsTrue(tmp);
+ if (j == -1)
+ return NULL;
+ else if (j == 1)
+ SETFLAG(flags, i);
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "initial flags must be a dict");
+ return NULL;
+ }
+
+ if (pyignored == NULL) {
+ /* don't copy ignored flags from default context */
+ } else if (!PyDict_Check(pyignored)) {
+ for (i = 0; i < NUMSIGNALS; i++) {
+ j = PySequence_Contains(pyignored, errors[i]);
+ if (j == -1)
+ return NULL;
+ else if (j == 1)
+ SETFLAG(ignored, i);
+ }
+ } else {
+ for (i = 0; i < NUMSIGNALS; i++) {
+ j = PyDict_Contains(pyignored, errors[i]);
+ if (j == -1)
+ return NULL;
+ else if (j == 0)
+ continue;
+ tmp = PyDict_GetItem(pyignored, errors[i]);
+ if (!tmp)
+ return NULL;
+ j = PyObject_IsTrue(tmp);
+ if (j == -1)
+ return NULL;
+ else if (j == 1)
+ SETFLAG(ignored, i);
+ }
+ }
+
+ if (prec == LONG_MIN)
+ prec = PyDecimal_DefaultContext->prec;
+
+ if (Emin == LONG_MAX)
+ Emin = PyDecimal_DefaultContext->Emin;
+
+ if (Emax == LONG_MIN)
+ Emax = PyDecimal_DefaultContext->Emax;
+
+ if (capitals == -1)
+ capitals = PyDecimal_DefaultContext->capitals;
+ else
+ capitals = capitals & 1;
+
+ if (rounding_dec == -1)
+ rounding_dec = PyDecimal_DefaultContext->rounding_dec;
+
+ if (rounding_dec != NEVER_ROUND && rounding_dec != ALWAYS_ROUND) {
+ PyErr_SetString(PyExc_ValueError, "_rounding_decision must be one of "
+ "NEVER_ROUND or ALWAYS_ROUND");
+ return NULL;
+ }
+
+ if (rounding == -1)
+ rounding = PyDecimal_DefaultContext->rounding;
+
+ clamp = clamp & 1;
+
+ return (PyObject *)_new_contextobj(prec, rounding, rounding_dec, traps,
+ flags, Emin, Emax, capitals, clamp,
+ ignored);
+}
+
+static PyObject *
+context_get_flags(contextobject *self)
+{
+ /* XXX */
+ Py_RETURN_NONE;
+}
+
+static int
+context_set_flags(contextobject *self, PyObject *value)
+{
+ return 0;
+}
+
+static PyObject *
+context_get_traps(contextobject *self)
+{
+ Py_RETURN_NONE;
+}
+
+static int
+context_set_traps(contextobject *self, PyObject *value)
+{
+ return 0;
+}
+
+static PyGetSetDef context_getset[] = {
+ {"flags", (getter)context_get_flags, (setter)context_set_flags},
+ {"traps", (getter)context_get_traps, (setter)context_set_traps},
+ {NULL}
+};
+
+#define OFF(x) offsetof(contextobject, x)
+
+static PyMemberDef context_members[] = {
+ {"prec", T_LONG, OFF(prec), 0},
+ {"Emin", T_LONG, OFF(Emin), 0},
+ {"Emax", T_LONG, OFF(Emax), 0},
+ {"capitals", T_INT, OFF(capitals), 0},
+ {"rounding", T_INT, OFF(rounding), 0},
+ {NULL}
+};
+
+static PyTypeObject PyDecimal_DecimalContextType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ MODULE_NAME ".Context", /* tp_name */
+ sizeof(contextobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)context_dealloc,/* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)context_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ context_hash, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
+ context_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ (richcmpfunc)context_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ context_methods, /* tp_methods */
+ context_members, /* tp_members */
+ context_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ context_new, /* tp_new */
+ PyObject_Del, /* tp_free */
+};
+
+
+/* module methods and initialization *****************************************/
+
+static PyObject *
+module_getcontext(PyObject *self)
+{
+ return (PyObject *)getcontext();
+}
+
+static PyObject *
+module_setcontext(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"context", 0};
+ contextobject *ctx;
+ int res;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:setcontext", kwlist, &ctx))
+ return NULL;
+
+ if (!PyDecimalContext_Check(ctx)) {
+ PyErr_SetString(PyExc_TypeError, "setcontext() requires a Context object");
+ return NULL;
+ }
+
+ if (ctx == PyDecimal_BasicContext ||
+ ctx == PyDecimal_ExtendedContext ||
+ ctx == PyDecimal_DefaultContext) {
+
+ ctx = (contextobject *)context_copy(ctx);
+ res = setcontext(ctx);
+ Py_DECREF(ctx);
+ if (res == -1) return NULL;
+ else Py_RETURN_NONE;
+ }
+
+ res = setcontext(ctx);
+ if (res == -1) return NULL;
+ else Py_RETURN_NONE;
+}
+
+static PyMethodDef module_methods[] = {
+ {"getcontext", (PyCFunction)module_getcontext, METH_NOARGS},
+ {"setcontext", (PyCFunction)module_setcontext, METH_VARARGS | METH_KEYWORDS},
+ {NULL, NULL}
+};
+
+
+static int
+_add_handle_method(PyObject *tp, PyMethodDef *ml)
+{
+ PyObject *meth, *cfunc;
+ static PyObject *module;
+
+ if (!module) {
+ module = PyString_FromString(MODULE_NAME);
+ if (!module)
+ return -1;
+ }
+
+ cfunc = PyCFunction_NewEx(ml, NULL, module);
+ if (!cfunc)
+ return -1;
+
+ meth = PyMethod_New(cfunc, NULL, tp);
+ if (!meth) {
+ Py_DECREF(cfunc);
+ return -1;
+ }
+
+ PyObject_SetAttrString(tp, "handle", meth);
+ Py_DECREF(cfunc);
+ Py_DECREF(meth);
+ return 0;
+}
+
+#define ADD_CONST(m, name) \
+ if (PyModule_AddIntConstant(m, #name, name) < 0) { return; }
+
+#define INIT_EXC(m, name, base) \
+ name = PyErr_NewException(MODULE_NAME "." #name, base, NULL); \
+ if (!name) return; \
+ Py_INCREF(name); \
+ if (PyModule_AddObject(m, #name, name) < 0) { return; }
+
+#define INIT_EXC_WITH_FUNC(m, name, base) \
+ INIT_EXC(m, name, base) \
+ if (_add_handle_method(name, &ml_##name) < 0) { return; }
+
+#define INIT_EXC_WITH_FUNC_AND_2TUPLE(m, name, base1, base2) \
+ tup = PyTuple_Pack(2, base1, base2); \
+ if (!tup) return; \
+ INIT_EXC_WITH_FUNC(m, name, tup) \
+ Py_DECREF(tup)
+
+#define INIT_EXC_WITH_3TUPLE(m, name, base1, base2, base3) \
+ tup = PyTuple_Pack(3, base1, base2, base3); \
+ if (!tup) return; \
+ INIT_EXC(m, name, tup)
+
+
+PyMODINIT_FUNC
+INITFUNC_NAME(void)
+{
+ PyObject *m; /* a module object */
+ PyObject *tup;
+ contextobject *ctx;
+ int traps = 0;
+
+ m = Py_InitModule3(MODULE_NAME, module_methods,
+ "Fast implementation of decimal arithmetic.");
+ if (m == NULL)
+ return;
+
+ if (PyType_Ready(&PyDecimal_DecimalType) < 0)
+ return;
+ if (PyType_Ready(&PyDecimal_DecimalContextType) < 0)
+ return;
+
+ Py_INCREF(&PyDecimal_DecimalType);
+ PyModule_AddObject(m, "Decimal", (PyObject *) &PyDecimal_DecimalType);
+
+ Py_INCREF(&PyDecimal_DecimalContextType);
+ PyModule_AddObject(m, "Context", (PyObject *) &PyDecimal_DecimalContextType);
+
+ SETFLAG(traps, S_DIV_BY_ZERO);
+ SETFLAG(traps, S_OVERFLOW);
+ SETFLAG(traps, S_INV_OPERATION);
+ PyDecimal_DefaultContext = _new_contextobj(28, ROUND_HALF_EVEN, ALWAYS_ROUND,
+ traps, 0, -999999999L, 999999999L, 1, 0, 0);
+ if (!PyDecimal_DefaultContext)
+ return;
+ if (PyModule_AddObject(m, "DefaultContext",
+ (PyObject *)PyDecimal_DefaultContext) < 0)
+ return;
+
+ /* add flags for BasicContext */
+ SETFLAG(traps, S_CLAMPED);
+ SETFLAG(traps, S_UNDERFLOW);
+
+ PyDecimal_BasicContext = _new_contextobj(9, ROUND_HALF_UP, ALWAYS_ROUND,
+ traps, 0, -999999999L, 999999999L, 1, 0, 0);
+ if (!PyDecimal_BasicContext)
+ return;
+ if (PyModule_AddObject(m, "BasicContext",
+ (PyObject *)PyDecimal_BasicContext) < 0)
+ return;
+
+ PyDecimal_ExtendedContext = _new_contextobj(9, ROUND_HALF_EVEN, ALWAYS_ROUND,
+ 0, 0, -999999999L, 999999999L, 1, 0, 0);
+ if (!PyDecimal_ExtendedContext)
+ return;
+ if (PyModule_AddObject(m, "ExtendedContext",
+ (PyObject *)PyDecimal_ExtendedContext) < 0)
+ return;
+
+ ctx = getcontext();
+ if (!ctx) return;
+
+ PyDecimal_NaN = PyDecimal_FromString("nan", 3, ctx);
+ if (!PyDecimal_NaN)
+ return;
+ PyDecimal_Inf = PyDecimal_FromString("inf", 3, ctx);
+ if (!PyDecimal_Inf)
+ return;
+ PyDecimal_NegInf = PyDecimal_FromString("-inf", 4, ctx);
+ if (!PyDecimal_NegInf)
+ return;
+
+ ADD_CONST(m, ROUND_DOWN);
+ ADD_CONST(m, ROUND_UP);
+ ADD_CONST(m, ROUND_HALF_DOWN);
+ ADD_CONST(m, ROUND_HALF_EVEN);
+ ADD_CONST(m, ROUND_HALF_UP);
+ ADD_CONST(m, ROUND_FLOOR);
+ ADD_CONST(m, ROUND_CEILING);
+
+ INIT_EXC_WITH_FUNC(m, DecimalException, PyExc_ArithmeticError);
+ INIT_EXC(m, Clamped, DecimalException);
+ INIT_EXC_WITH_FUNC(m, InvalidOperation, DecimalException);
+ INIT_EXC_WITH_FUNC(m, ConversionSyntax, InvalidOperation);
+ INIT_EXC_WITH_FUNC(m, DivisionImpossible, InvalidOperation);
+ INIT_EXC_WITH_FUNC_AND_2TUPLE(m, DivisionUndefined,
+ InvalidOperation, PyExc_ZeroDivisionError);
+ INIT_EXC_WITH_FUNC(m, InvalidContext, InvalidOperation);
+ INIT_EXC_WITH_FUNC_AND_2TUPLE(m, DivisionByZero,
+ DecimalException, PyExc_ZeroDivisionError);
+ INIT_EXC(m, Inexact, DecimalException);
+ INIT_EXC(m, Rounded, DecimalException);
+ INIT_EXC(m, Subnormal, DecimalException);
+ INIT_EXC_WITH_FUNC_AND_2TUPLE(m, Overflow, Rounded, Inexact);
+ INIT_EXC_WITH_3TUPLE(m, Underflow,
+ Rounded, Inexact, Subnormal);
+
+ errors[S_INV_OPERATION] = InvalidOperation;
+ errors[S_DIV_BY_ZERO] = DivisionByZero;
+ errors[S_CLAMPED] = Clamped;
+ errors[S_INEXACT] = Inexact;
+ errors[S_ROUNDED] = Rounded;
+ errors[S_SUBNORMAL] = Subnormal;
+ errors[S_OVERFLOW] = Overflow;
+ errors[S_UNDERFLOW] = Underflow;
+ errors[C_INV_CONTEXT] = InvalidContext;
+ errors[C_CONV_SYNTAX] = ConversionSyntax;
+ errors[C_DIV_IMPOSSIBLE]= DivisionImpossible;
+ errors[C_DIV_UNDEFINED] = DivisionUndefined;
+}
+
Added: sandbox/trunk/decimal-c/decimal.h
==============================================================================
--- (empty file)
+++ sandbox/trunk/decimal-c/decimal.h Mon May 22 17:47:15 2006
@@ -0,0 +1,66 @@
+/* decimal.h */
+
+#ifndef PY_DECIMAL_H
+#define PY_DECIMAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* decimalobject struct ******************************************************/
+
+typedef struct {
+ PyObject_VAR_HEAD
+ unsigned int sign; /* see sign contstants above */
+ long exp;
+ signed char digits[1]; /* digits are stored as the actual numbers 0-9 */
+} PyDecimalObject;
+
+static PyTypeObject PyDecimal_DecimalType;
+
+/* contextobject struct ******************************************************/
+
+typedef struct {
+ PyObject_HEAD
+ long prec;
+
+ /* option flags */
+ int rounding; /* see rounding constants above */
+ int rounding_dec; /* dito */
+ int capitals; /* print "e" or "E" before exponents */
+ int clamp; /* change exponents if they are too high */
+
+ /* min/max exponents */
+ long Emin;
+ long Emax;
+
+ /* signal handling -- these are bit fields */
+ int traps; /* if a signal is trapped */
+ int ignored; /* if a signal is ignored completely */
+ int flags; /* if a signal has occurred, but not been trapped */
+} PyDecimalContextObject;
+
+static PyTypeObject PyDecimal_DecimalContextType;
+
+/* type checking macros ******************************************************/
+
+#define PyDecimal_Check(op) PyObject_TypeCheck((op), &PyDecimal_DecimalType)
+#define PyDecimalContext_Check(op) \
+ PyObject_TypeCheck((op), &PyDecimal_DecimalContextType)
+
+/* static constants **********************************************************/
+
+static PyObject *PyDecimal_NaN;
+static PyObject *PyDecimal_Inf;
+static PyObject *PyDecimal_NegInf;
+
+static PyDecimalContextObject *PyDecimal_DefaultContext;
+static PyDecimalContextObject *PyDecimal_BasicContext;
+static PyDecimalContextObject *PyDecimal_ExtendedContext;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* defined(PY_DECIMAL_H) */
More information about the Python-checkins
mailing list