[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