Index: Python/bltinmodule.c =================================================================== --- Python/bltinmodule.c (revision 59590) +++ Python/bltinmodule.c (working copy) @@ -1926,39 +1926,31 @@ static PyObject * builtin_round(PyObject *self, PyObject *args, PyObject *kwds) { - double number; - double f; - int ndigits = 0; - int i; +#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */ + int ndigits = UNDEF_NDIGITS; static char *kwlist[] = {"number", "ndigits", 0}; + PyObject *number; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|i:round", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i:round", kwlist, &number, &ndigits)) return NULL; - f = 1.0; - i = abs(ndigits); - while (--i >= 0) - f = f*10.0; - if (ndigits < 0) - number /= f; - else - number *= f; - if (number >= 0.0) - number = floor(number + 0.5); - else - number = ceil(number - 0.5); - if (ndigits < 0) - number *= f; - else - number /= f; - return PyFloat_FromDouble(number); + + // The py3k branch gets better errors for this by using + // _PyType_Lookup(), but since float's mro isn't set in py2.6, + // we just use PyObject_CallMethod here. + if (ndigits == UNDEF_NDIGITS) + return PyObject_CallMethod(number, "__round__", ""); + else + return PyObject_CallMethod(number, "__round__", "i", ndigits); +#undef UNDEF_NDIGITS } PyDoc_STRVAR(round_doc, "round(number[, ndigits]) -> floating point number\n\ \n\ Round a number to a given precision in decimal digits (default 0 digits).\n\ -This always returns a floating point number. Precision may be negative."); +This returns an int when called with one argument, otherwise a float.\n\ +Precision may be negative."); static PyObject * builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds) @@ -2039,7 +2031,21 @@ Without arguments, equivalent to locals().\n\ With an argument, equivalent to object.__dict__."); +static PyObject * +builtin_trunc(PyObject *self, PyObject *number) +{ + // XXX: The py3k branch gets better errors for this by using + // _PyType_Lookup(), but since float's mro isn't set in py2.6, + // we just use PyObject_CallMethod here. + return PyObject_CallMethod(number, "__trunc__", ""); +} +PyDoc_STRVAR(trunc_doc, +"trunc(Real) -> Integral\n\ +\n\ +returns the integral closest to x between 0 and x."); + + static PyObject* builtin_sum(PyObject *self, PyObject *args) { @@ -2387,6 +2393,7 @@ {"unichr", builtin_unichr, METH_VARARGS, unichr_doc}, #endif {"vars", builtin_vars, METH_VARARGS, vars_doc}, + {"trunc", builtin_trunc, METH_O, trunc_doc}, {"zip", builtin_zip, METH_VARARGS, zip_doc}, {NULL, NULL}, }; Index: Objects/complexobject.c =================================================================== --- Objects/complexobject.c (revision 59590) +++ Objects/complexobject.c (working copy) @@ -385,6 +385,41 @@ return combined; } +/* This macro may return! */ +#define TO_COMPLEX(obj, c) \ + if (PyComplex_Check(obj)) \ + c = ((PyComplexObject *)(obj))->cval; \ + else if (to_complex(&(obj), &(c)) < 0) \ + return (obj) + +static int +to_complex(PyObject **pobj, Py_complex *pc) +{ + PyObject *obj = *pobj; + + pc->real = pc->imag = 0.0; + if (PyInt_Check(obj)) { + pc->real = PyInt_AS_LONG(obj); + return 0; + } + if (PyLong_Check(obj)) { + pc->real = PyLong_AsDouble(obj); + if (pc->real == -1.0 && PyErr_Occurred()) { + *pobj = NULL; + return -1; + } + return 0; + } + if (PyFloat_Check(obj)) { + pc->real = PyFloat_AsDouble(obj); + return 0; + } + Py_INCREF(Py_NotImplemented); + *pobj = Py_NotImplemented; + return -1; +} + + static PyObject * complex_add(PyComplexObject *v, PyComplexObject *w) { @@ -502,24 +537,27 @@ } static PyObject * -complex_pow(PyComplexObject *v, PyObject *w, PyComplexObject *z) +complex_pow(PyObject *v, PyObject *w, PyObject *z) { Py_complex p; Py_complex exponent; long int_exponent; + Py_complex a, b; + TO_COMPLEX(v, a); + TO_COMPLEX(w, b); - if ((PyObject *)z!=Py_None) { + if (z!=Py_None) { PyErr_SetString(PyExc_ValueError, "complex modulo"); return NULL; } PyFPE_START_PROTECT("complex_pow", return 0) errno = 0; - exponent = ((PyComplexObject*)w)->cval; + exponent = b; int_exponent = (long)exponent.real; if (exponent.imag == 0. && exponent.real == int_exponent) - p = c_powi(v->cval,int_exponent); + p = c_powi(a,int_exponent); else - p = c_pow(v->cval,exponent); + p = c_pow(a,exponent); PyFPE_END_PROTECT(p) Py_ADJUST_ERANGE2(p.real, p.imag); @@ -541,6 +579,10 @@ { PyObject *t, *r; + if (PyErr_Warn(PyExc_DeprecationWarning, + "complex divmod(), // and % are deprecated") < 0) + return NULL; + t = complex_divmod(v, w); if (t != NULL) { r = PyTuple_GET_ITEM(t, 0); @@ -695,6 +737,11 @@ return PyComplex_FromCComplex(c); } +PyDoc_STRVAR(complex_conjugate_doc, +"complex.conjugate() -> complex\n" +"\n" +"Returns the complex conjugate of its argument. (3-4j).conjugate() == 3+4j."); + static PyObject * complex_getnewargs(PyComplexObject *v) { @@ -702,7 +749,8 @@ } static PyMethodDef complex_methods[] = { - {"conjugate", (PyCFunction)complex_conjugate, METH_NOARGS}, + {"conjugate", (PyCFunction)complex_conjugate, METH_NOARGS, + complex_conjugate_doc}, {"__getnewargs__", (PyCFunction)complex_getnewargs, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; Index: Objects/intobject.c =================================================================== --- Objects/intobject.c (revision 59590) +++ Objects/intobject.c (working copy) @@ -4,6 +4,8 @@ #include "Python.h" #include +static PyObject *int_int(PyIntObject *v); + long PyInt_GetMax(void) { @@ -783,21 +785,10 @@ } static PyObject * -int_pos(PyIntObject *v) -{ - if (PyInt_CheckExact(v)) { - Py_INCREF(v); - return (PyObject *)v; - } - else - return PyInt_FromLong(v->ob_ival); -} - -static PyObject * int_abs(PyIntObject *v) { if (v->ob_ival >= 0) - return int_pos(v); + return int_int(v); else return int_neg(v); } @@ -827,7 +818,7 @@ return NULL; } if (a == 0 || b == 0) - return int_pos(v); + return int_int(v); if (b >= LONG_BIT) { vv = PyLong_FromLong(PyInt_AS_LONG(v)); if (vv == NULL) @@ -871,7 +862,7 @@ return NULL; } if (a == 0 || b == 0) - return int_pos(v); + return int_int(v); if (b >= LONG_BIT) { if (a < 0) a = -1; @@ -1060,11 +1051,72 @@ return Py_BuildValue("(l)", v->ob_ival); } +static PyObject * +int_getN(PyIntObject *v, void *context) { + return PyInt_FromLong((intptr_t)context); +} + +static PyObject * +int_round(PyObject *self, PyObject *args) +{ +#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */ + int ndigits = UNDEF_NDIGITS; + double x; + PyObject *res; + + if (!PyArg_ParseTuple(args, "|i", &ndigits)) + return NULL; + + if (ndigits == UNDEF_NDIGITS) + return int_int((PyIntObject *)self); + + /* If called with two args, defer to float.__round__(). */ + x = (double) PyInt_AS_LONG(self); + self = PyFloat_FromDouble(x); + if (self == NULL) + return NULL; + res = PyObject_CallMethod(self, "__round__", "i", ndigits); + Py_DECREF(self); + return res; +#undef UNDEF_NDIGITS +} + static PyMethodDef int_methods[] = { + {"conjugate", (PyCFunction)int_int, METH_NOARGS, + "Returns self, the complex conjugate of any int."}, + {"__trunc__", (PyCFunction)int_int, METH_NOARGS, + "Truncating an Integral returns itself."}, + {"__floor__", (PyCFunction)int_int, METH_NOARGS, + "Flooring an Integral returns itself."}, + {"__ceil__", (PyCFunction)int_int, METH_NOARGS, + "Ceiling of an Integral returns itself."}, + {"__round__", (PyCFunction)int_round, METH_VARARGS, + "Rounding an Integral returns itself.\n" + "Rounding with an ndigits arguments defers to float.__round__."}, {"__getnewargs__", (PyCFunction)int_getnewargs, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; +static PyGetSetDef int_getset[] = { + {"real", + (getter)int_int, (setter)NULL, + "the real part of a complex number", + NULL}, + {"imag", + (getter)int_getN, (setter)NULL, + "the imaginary part of a complex number", + (void*)0}, + {"numerator", + (getter)int_int, (setter)NULL, + "the numerator of a rational number in lowest terms", + NULL}, + {"denominator", + (getter)int_getN, (setter)NULL, + "the denominator of a rational number in lowest terms", + (void*)1}, + {NULL} /* Sentinel */ +}; + PyDoc_STRVAR(int_doc, "int(x[, base]) -> integer\n\ \n\ @@ -1085,7 +1137,7 @@ (binaryfunc)int_divmod, /*nb_divmod*/ (ternaryfunc)int_pow, /*nb_power*/ (unaryfunc)int_neg, /*nb_negative*/ - (unaryfunc)int_pos, /*nb_positive*/ + (unaryfunc)int_int, /*nb_positive*/ (unaryfunc)int_abs, /*nb_absolute*/ (inquiry)int_nonzero, /*nb_nonzero*/ (unaryfunc)int_invert, /*nb_invert*/ @@ -1149,7 +1201,7 @@ 0, /* tp_iternext */ int_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + int_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ Index: Objects/longobject.c =================================================================== --- Objects/longobject.c (revision 59590) +++ Objects/longobject.c (working copy) @@ -1716,7 +1716,7 @@ /* forward */ static PyLongObject *x_divrem (PyLongObject *, PyLongObject *, PyLongObject **); -static PyObject *long_pos(PyLongObject *); +static PyObject *long_long(PyObject *v); static int long_divrem(PyLongObject *, PyLongObject *, PyLongObject **, PyLongObject **); @@ -2906,17 +2906,6 @@ } static PyObject * -long_pos(PyLongObject *v) -{ - if (PyLong_CheckExact(v)) { - Py_INCREF(v); - return (PyObject *)v; - } - else - return _PyLong_Copy(v); -} - -static PyObject * long_neg(PyLongObject *v) { PyLongObject *z; @@ -2937,7 +2926,7 @@ if (v->ob_size < 0) return long_neg(v); else - return long_pos(v); + return long_long((PyObject *)v); } static int @@ -3373,11 +3362,74 @@ return Py_BuildValue("(N)", _PyLong_Copy(v)); } +static PyObject * +long_getN(PyLongObject *v, void *context) { + return PyLong_FromLong((intptr_t)context); +} + +static PyObject * +long_round(PyObject *self, PyObject *args) +{ +#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */ + int ndigits = UNDEF_NDIGITS; + double x; + PyObject *res; + + if (!PyArg_ParseTuple(args, "|i", &ndigits)) + return NULL; + + if (ndigits == UNDEF_NDIGITS) + return long_long(self); + + /* If called with two args, defer to float.__round__(). */ + x = PyLong_AsDouble(self); + if (x == -1.0 && PyErr_Occurred()) + return NULL; + self = PyFloat_FromDouble(x); + if (self == NULL) + return NULL; + res = PyObject_CallMethod(self, "__round__", "i", ndigits); + Py_DECREF(self); + return res; +#undef UNDEF_NDIGITS +} + static PyMethodDef long_methods[] = { + {"conjugate", (PyCFunction)long_long, METH_NOARGS, + "Returns self, the complex conjugate of any long."}, + {"__trunc__", (PyCFunction)long_long, METH_NOARGS, + "Truncating an Integral returns itself."}, + {"__floor__", (PyCFunction)long_long, METH_NOARGS, + "Flooring an Integral returns itself."}, + {"__ceil__", (PyCFunction)long_long, METH_NOARGS, + "Ceiling of an Integral returns itself."}, + {"__round__", (PyCFunction)long_round, METH_VARARGS, + "Rounding an Integral returns itself.\n" + "Rounding with an ndigits arguments defers to float.__round__."}, {"__getnewargs__", (PyCFunction)long_getnewargs, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; +static PyGetSetDef long_getset[] = { + {"real", + (getter)long_long, (setter)NULL, + "the real part of a complex number", + NULL}, + {"imag", + (getter)long_getN, (setter)NULL, + "the imaginary part of a complex number", + (void*)0}, + {"numerator", + (getter)long_long, (setter)NULL, + "the numerator of a rational number in lowest terms", + NULL}, + {"denominator", + (getter)long_getN, (setter)NULL, + "the denominator of a rational number in lowest terms", + (void*)1}, + {NULL} /* Sentinel */ +}; + PyDoc_STRVAR(long_doc, "long(x[, base]) -> integer\n\ \n\ @@ -3396,7 +3448,7 @@ long_divmod, /*nb_divmod*/ long_pow, /*nb_power*/ (unaryfunc) long_neg, /*nb_negative*/ - (unaryfunc) long_pos, /*tp_positive*/ + (unaryfunc) long_long, /*tp_positive*/ (unaryfunc) long_abs, /*tp_absolute*/ (inquiry) long_nonzero, /*tp_nonzero*/ (unaryfunc) long_invert, /*nb_invert*/ @@ -3461,7 +3513,7 @@ 0, /* tp_iternext */ long_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + long_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ Index: Objects/floatobject.c =================================================================== --- Objects/floatobject.c (revision 59590) +++ Objects/floatobject.c (working copy) @@ -986,9 +986,10 @@ * bugs so we have to figure it out ourselves. */ if (iw != floor(iw)) { - PyErr_SetString(PyExc_ValueError, "negative number " - "cannot be raised to a fractional power"); - return NULL; + /* Negative numbers raised to fractional powers + * become complex. + */ + return PyComplex_Type.tp_as_number->nb_power(v, w, z); } /* iw is an exact integer, albeit perhaps a very large one. * -1 raised to an exact integer should never be exceptional. @@ -1035,17 +1036,6 @@ } static PyObject * -float_pos(PyFloatObject *v) -{ - if (PyFloat_CheckExact(v)) { - Py_INCREF(v); - return (PyObject *)v; - } - else - return PyFloat_FromDouble(v->ob_fval); -} - -static PyObject * float_abs(PyFloatObject *v) { return PyFloat_FromDouble(fabs(v->ob_fval)); @@ -1083,16 +1073,9 @@ } static PyObject * -float_long(PyObject *v) +float_trunc(PyObject *v) { double x = PyFloat_AsDouble(v); - return PyLong_FromDouble(x); -} - -static PyObject * -float_int(PyObject *v) -{ - double x = PyFloat_AsDouble(v); double wholepart; /* integral portion of x, rounded toward 0 */ (void)modf(x, &wholepart); @@ -1116,6 +1099,55 @@ } static PyObject * +float_round(PyObject *v, PyObject *args) +{ +#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */ + double x; + double f; + double flr, cil; + double rounded; + int i; + int ndigits = UNDEF_NDIGITS; + + if (!PyArg_ParseTuple(args, "|i", &ndigits)) + return NULL; + + x = PyFloat_AsDouble(v); + + if (ndigits != UNDEF_NDIGITS) { + f = 1.0; + i = abs(ndigits); + while (--i >= 0) + f = f*10.0; + if (ndigits < 0) + x /= f; + else + x *= f; + } + + flr = floor(x); + cil = ceil(x); + + if (x-flr > 0.5) + rounded = cil; + else if (x-flr == 0.5) + rounded = fmod(flr, 2) == 0 ? flr : cil; + else + rounded = flr; + + if (ndigits != UNDEF_NDIGITS) { + if (ndigits < 0) + rounded *= f; + else + rounded /= f; + return PyFloat_FromDouble(rounded); + } + + return PyLong_FromDouble(rounded); +#undef UNDEF_NDIGITS +} + +static PyObject * float_float(PyObject *v) { if (PyFloat_CheckExact(v)) @@ -1302,7 +1334,20 @@ "Overrides the automatic determination of C-level floating point type.\n" "This affects how floats are converted to and from binary strings."); +static PyObject * +float_getzero(PyObject *v, void *closure) +{ + return PyFloat_FromDouble(0.0); +} + static PyMethodDef float_methods[] = { + {"conjugate", (PyCFunction)float_float, METH_NOARGS, + "Returns self, the complex conjugate of any float."}, + {"__trunc__", (PyCFunction)float_trunc, METH_NOARGS, + "Returns the Integral closest to x between 0 and x."}, + {"__round__", (PyCFunction)float_round, METH_VARARGS, + "Returns the Integral closest to x, rounding half toward even.\n" + "When an argument is passed, works like built-in round(x, ndigits)."}, {"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS}, {"__getformat__", (PyCFunction)float_getformat, METH_O|METH_CLASS, float_getformat_doc}, @@ -1311,6 +1356,18 @@ {NULL, NULL} /* sentinel */ }; +static PyGetSetDef float_getset[] = { + {"real", + (getter)float_float, (setter)NULL, + "the real part of a complex number", + NULL}, + {"imag", + (getter)float_getzero, (setter)NULL, + "the imaginary part of a complex number", + NULL}, + {NULL} /* Sentinel */ +}; + PyDoc_STRVAR(float_doc, "float(x) -> floating point number\n\ \n\ @@ -1326,7 +1383,7 @@ float_divmod, /*nb_divmod*/ float_pow, /*nb_power*/ (unaryfunc)float_neg, /*nb_negative*/ - (unaryfunc)float_pos, /*nb_positive*/ + (unaryfunc)float_float, /*nb_positive*/ (unaryfunc)float_abs, /*nb_absolute*/ (inquiry)float_nonzero, /*nb_nonzero*/ 0, /*nb_invert*/ @@ -1336,8 +1393,8 @@ 0, /*nb_xor*/ 0, /*nb_or*/ float_coerce, /*nb_coerce*/ - float_int, /*nb_int*/ - float_long, /*nb_long*/ + float_trunc, /*nb_int*/ + float_trunc, /*nb_long*/ float_float, /*nb_float*/ 0, /* nb_oct */ 0, /* nb_hex */ @@ -1389,7 +1446,7 @@ 0, /* tp_iternext */ float_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + float_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ Index: Lib/numbers.py =================================================================== --- Lib/numbers.py (revision 0) +++ Lib/numbers.py (revision 0) @@ -0,0 +1,393 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141. + +TODO: Fill out more detailed documentation on the operators.""" + +from abc import ABCMeta, abstractmethod, abstractproperty + +__all__ = ["Number", "Exact", "Inexact", + "Complex", "Real", "Rational", "Integral", + ] + + +class Number(object): + """All numbers inherit from this class. + + If you just want to check if an argument x is a number, without + caring what kind, use isinstance(x, Number). + """ + __metaclass__ = ABCMeta + + +class Exact(Number): + """Operations on instances of this type are exact. + + As long as the result of a homogenous operation is of the same + type, you can assume that it was computed exactly, and there are + no round-off errors. Laws like commutativity and associativity + hold. + """ + +Exact.register(int) +Exact.register(long) + + +class Inexact(Number): + """Operations on instances of this type are inexact. + + Given X, an instance of Inexact, it is possible that (X + -X) + 3 + == 3, but X + (-X + 3) == 0. The exact form this error takes will + vary by type, but it's generally unsafe to compare this type for + equality. + """ + +Inexact.register(complex) +Inexact.register(float) +# Inexact.register(decimal.Decimal) + + +class Complex(Number): + """Complex defines the operations that work on the builtin complex type. + + In short, those are: a conversion to complex, .real, .imag, +, -, + *, /, abs(), .conjugate, ==, and !=. + + If it is given heterogenous arguments, and doesn't have special + knowledge about them, it should fall back to the builtin complex + type as described below. + """ + + @abstractmethod + def __complex__(self): + """Return a builtin complex instance. Called for complex(self).""" + + def __bool__(self): + """True if self != 0. Called for bool(self).""" + return self != 0 + + @abstractproperty + def real(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractproperty + def imag(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractmethod + def __add__(self, other): + """self + other""" + raise NotImplementedError + + @abstractmethod + def __radd__(self, other): + """other + self""" + raise NotImplementedError + + @abstractmethod + def __neg__(self): + """-self""" + raise NotImplementedError + + def __pos__(self): + """+self""" + raise NotImplementedError + + def __sub__(self, other): + """self - other""" + return self + -other + + def __rsub__(self, other): + """other - self""" + return -self + other + + @abstractmethod + def __mul__(self, other): + """self * other""" + raise NotImplementedError + + @abstractmethod + def __rmul__(self, other): + """other * self""" + raise NotImplementedError + + @abstractmethod + def __div__(self, other): + """self / other; should promote to float or complex when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rdiv__(self, other): + """other / self""" + raise NotImplementedError + + @abstractmethod + def __pow__(self, exponent): + """self**exponent; should promote to float or complex when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rpow__(self, base): + """base ** self""" + raise NotImplementedError + + @abstractmethod + def __abs__(self): + """Returns the Real distance from 0. Called for abs(self).""" + raise NotImplementedError + + @abstractmethod + def conjugate(self): + """(x+y*i).conjugate() returns (x-y*i).""" + raise NotImplementedError + + @abstractmethod + def __eq__(self, other): + """self == other""" + raise NotImplementedError + + # __ne__ is inherited from object and negates whatever __eq__ does. + +Complex.register(complex) + + +class Real(Complex): + """To Complex, Real adds the operations that work on real numbers. + + In short, those are: a conversion to float, trunc(), divmod, + %, <, <=, >, and >=. + + Real also provides defaults for the derived operations. + """ + + @abstractmethod + def __float__(self): + """Any Real can be converted to a native float object. + + Called for float(self).""" + raise NotImplementedError + + @abstractmethod + def __trunc__(self): + """trunc(self): Truncates self to an Integral. + + Returns an Integral i such that: + * i>0 iff self>0; + * abs(i) <= abs(self); + * for any Integral j satisfying the first two conditions, + abs(i) >= abs(j) [i.e. i has "maximal" abs among those]. + i.e. "truncate towards 0". + """ + raise NotImplementedError + + @abstractmethod + def __floor__(self): + """Finds the greatest Integral <= self.""" + raise NotImplementedError + + @abstractmethod + def __ceil__(self): + """Finds the least Integral >= self.""" + raise NotImplementedError + + @abstractmethod + def __round__(self, ndigits=None): + """Rounds self to ndigits decimal places, defaulting to 0. + + If ndigits is omitted or None, returns an Integral, otherwise + returns a Real. Rounds half toward even. + """ + raise NotImplementedError + + def __divmod__(self, other): + """divmod(self, other): The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (self // other, self % other) + + def __rdivmod__(self, other): + """divmod(other, self): The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (other // self, other % self) + + @abstractmethod + def __floordiv__(self, other): + """self // other: The floor() of self/other.""" + raise NotImplementedError + + @abstractmethod + def __rfloordiv__(self, other): + """other // self: The floor() of other/self.""" + raise NotImplementedError + + @abstractmethod + def __mod__(self, other): + """self % other""" + raise NotImplementedError + + @abstractmethod + def __rmod__(self, other): + """other % self""" + raise NotImplementedError + + @abstractmethod + def __lt__(self, other): + """self < other + + < on Reals defines a total ordering, except perhaps for NaN.""" + raise NotImplementedError + + @abstractmethod + def __le__(self, other): + """self <= other""" + raise NotImplementedError + + # Concrete implementations of Complex abstract methods. + def __complex__(self): + """complex(self) == complex(float(self), 0)""" + return complex(float(self)) + + @property + def real(self): + """Real numbers are their real component.""" + return +self + + @property + def imag(self): + """Real numbers have no imaginary component.""" + return 0 + + def conjugate(self): + """Conjugate is a no-op for Reals.""" + return +self + +Real.register(float) +# Real.register(decimal.Decimal) + + +class Rational(Real, Exact): + """.numerator and .denominator should be in lowest terms.""" + + @abstractproperty + def numerator(self): + raise NotImplementedError + + @abstractproperty + def denominator(self): + raise NotImplementedError + + # Concrete implementation of Real's conversion to float. + def __float__(self): + """float(self) = self.numerator / self.denominator""" + return self.numerator / self.denominator + + +class Integral(Rational): + """Integral adds a conversion to long and the bit-string operations.""" + + @abstractmethod + def __long__(self): + """long(self)""" + raise NotImplementedError + + def __index__(self): + """index(self)""" + return long(self) + + @abstractmethod + def __pow__(self, exponent, modulus=None): + """self ** exponent % modulus, but maybe faster. + + Accept the modulus argument if you want to support the + 3-argument version of pow(). Raise a TypeError if exponent < 0 + or any argument isn't Integral. Otherwise, just implement the + 2-argument version described in Complex. + """ + raise NotImplementedError + + @abstractmethod + def __lshift__(self, other): + """self << other""" + raise NotImplementedError + + @abstractmethod + def __rlshift__(self, other): + """other << self""" + raise NotImplementedError + + @abstractmethod + def __rshift__(self, other): + """self >> other""" + raise NotImplementedError + + @abstractmethod + def __rrshift__(self, other): + """other >> self""" + raise NotImplementedError + + @abstractmethod + def __and__(self, other): + """self & other""" + raise NotImplementedError + + @abstractmethod + def __rand__(self, other): + """other & self""" + raise NotImplementedError + + @abstractmethod + def __xor__(self, other): + """self ^ other""" + raise NotImplementedError + + @abstractmethod + def __rxor__(self, other): + """other ^ self""" + raise NotImplementedError + + @abstractmethod + def __or__(self, other): + """self | other""" + raise NotImplementedError + + @abstractmethod + def __ror__(self, other): + """other | self""" + raise NotImplementedError + + @abstractmethod + def __invert__(self): + """~self""" + raise NotImplementedError + + # Concrete implementations of Rational and Real abstract methods. + def __float__(self): + """float(self) == float(long(self))""" + return float(long(self)) + + @property + def numerator(self): + """Integers are their own numerators.""" + return +self + + @property + def denominator(self): + """Integers have a denominator of 1.""" + return 1 + +Integral.register(int) +Integral.register(long) Index: Lib/unittest.py =================================================================== --- Lib/unittest.py (revision 59590) +++ Lib/unittest.py (working copy) @@ -358,7 +358,7 @@ Note that decimal places (from zero) are usually not the same as significant digits (measured from the most signficant digit). """ - if round(second-first, places) != 0: + if round(abs(second-first), places) != 0: raise self.failureException, \ (msg or '%r != %r within %r places' % (first, second, places)) @@ -370,7 +370,7 @@ Note that decimal places (from zero) are usually not the same as significant digits (measured from the most signficant digit). """ - if round(second-first, places) == 0: + if round(abs(second-first), places) == 0: raise self.failureException, \ (msg or '%r == %r within %r places' % (first, second, places)) Index: Lib/test/test_math.py =================================================================== --- Lib/test/test_math.py (revision 59590) +++ Lib/test/test_math.py (working copy) @@ -58,6 +58,19 @@ self.ftest('ceil(-1.0)', math.ceil(-1.0), -1) self.ftest('ceil(-1.5)', math.ceil(-1.5), -1) + class TestCeil(object): + def __ceil__(self): + return 42 + class TestNoCeil(object): + pass + self.ftest('ceil(TestCeil())', math.ceil(TestCeil()), 42) + self.assertRaises(TypeError, math.ceil, TestNoCeil()) + + t = TestNoCeil() + t.__ceil__ = lambda *args: args + self.assertRaises(TypeError, math.ceil, t) + self.assertRaises(TypeError, math.ceil, t, 0) + def testCos(self): self.assertRaises(TypeError, math.cos) self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0) @@ -101,6 +114,19 @@ self.ftest('floor(1.23e167)', math.floor(1.23e167), 1.23e167) self.ftest('floor(-1.23e167)', math.floor(-1.23e167), -1.23e167) + class TestFloor(object): + def __floor__(self): + return 42 + class TestNoFloor(object): + pass + self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42) + self.assertRaises(TypeError, math.floor, TestNoFloor()) + + t = TestNoFloor() + t.__floor__ = lambda *args: args + self.assertRaises(TypeError, math.floor, t) + self.assertRaises(TypeError, math.floor, t, 0) + def testFmod(self): self.assertRaises(TypeError, math.fmod) self.ftest('fmod(10,1)', math.fmod(10,1), 0) Index: Lib/test/test_long.py =================================================================== --- Lib/test/test_long.py (revision 59590) +++ Lib/test/test_long.py (working copy) @@ -385,7 +385,9 @@ "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.", "math.sin(huge)", "math.sin(mhuge)", "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better - "math.floor(huge)", "math.floor(mhuge)"]: + # math.floor() of an int returns an int now + ##"math.floor(huge)", "math.floor(mhuge)", + ]: self.assertRaises(OverflowError, eval, test, namespace) Index: Lib/test/test_abstract_numbers.py =================================================================== --- Lib/test/test_abstract_numbers.py (revision 0) +++ Lib/test/test_abstract_numbers.py (revision 0) @@ -0,0 +1,62 @@ +"""Unit tests for numbers.py.""" + +import unittest +from test import test_support +from numbers import Number +from numbers import Exact, Inexact +from numbers import Complex, Real, Rational, Integral +import operator + +class TestNumbers(unittest.TestCase): + def test_int(self): + self.failUnless(issubclass(int, Integral)) + self.failUnless(issubclass(int, Complex)) + self.failUnless(issubclass(int, Exact)) + self.failIf(issubclass(int, Inexact)) + + self.assertEqual(7, int(7).real) + self.assertEqual(0, int(7).imag) + self.assertEqual(7, int(7).conjugate()) + self.assertEqual(7, int(7).numerator) + self.assertEqual(1, int(7).denominator) + + def test_long(self): + self.failUnless(issubclass(long, Integral)) + self.failUnless(issubclass(long, Complex)) + self.failUnless(issubclass(long, Exact)) + self.failIf(issubclass(long, Inexact)) + + self.assertEqual(7, long(7).real) + self.assertEqual(0, long(7).imag) + self.assertEqual(7, long(7).conjugate()) + self.assertEqual(7, long(7).numerator) + self.assertEqual(1, long(7).denominator) + + def test_float(self): + self.failIf(issubclass(float, Rational)) + self.failUnless(issubclass(float, Real)) + self.failIf(issubclass(float, Exact)) + self.failUnless(issubclass(float, Inexact)) + + self.assertEqual(7.3, float(7.3).real) + self.assertEqual(0, float(7.3).imag) + self.assertEqual(7.3, float(7.3).conjugate()) + + def test_complex(self): + self.failIf(issubclass(complex, Real)) + self.failUnless(issubclass(complex, Complex)) + self.failIf(issubclass(complex, Exact)) + self.failUnless(issubclass(complex, Inexact)) + + c1, c2 = complex(3, 2), complex(4,1) + # XXX: This is not ideal, but see the comment in builtin_trunc(). + self.assertRaises(AttributeError, trunc, c1) + self.assertRaises(TypeError, float, c1) + self.assertRaises(TypeError, int, c1) + +def test_main(): + test_support.run_unittest(TestNumbers) + + +if __name__ == "__main__": + unittest.main() Index: Lib/test/test_unittest.py =================================================================== --- Lib/test/test_unittest.py (revision 59590) +++ Lib/test/test_unittest.py (working copy) @@ -2264,13 +2264,34 @@ expected = ['startTest', 'test', 'stopTest'] self.assertEqual(events, expected) +class Test_Assertions(TestCase): + def test_AlmostEqual(self): + self.failUnlessAlmostEqual(1.00000001, 1.0) + self.failIfAlmostEqual(1.0000001, 1.0) + self.assertRaises(AssertionError, + self.failUnlessAlmostEqual, 1.0000001, 1.0) + self.assertRaises(AssertionError, + self.failIfAlmostEqual, 1.00000001, 1.0) + + self.failUnlessAlmostEqual(1.1, 1.0, places=0) + self.assertRaises(AssertionError, + self.failUnlessAlmostEqual, 1.1, 1.0, places=1) + + self.failUnlessAlmostEqual(0, .1+.1j, places=0) + self.failIfAlmostEqual(0, .1+.1j, places=1) + self.assertRaises(AssertionError, + self.failUnlessAlmostEqual, 0, .1+.1j, places=1) + self.assertRaises(AssertionError, + self.failIfAlmostEqual, 0, .1+.1j, places=0) + ###################################################################### ## Main ###################################################################### def test_main(): test_support.run_unittest(Test_TestCase, Test_TestLoader, - Test_TestSuite, Test_TestResult, Test_FunctionTestCase) + Test_TestSuite, Test_TestResult, Test_FunctionTestCase, + Test_Assertions) if __name__ == "__main__": test_main() Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 59590) +++ Lib/test/test_builtin.py (working copy) @@ -1450,11 +1450,13 @@ else: self.assertAlmostEqual(pow(x, y, z), 24.0) + self.assertAlmostEqual(pow(-1, 0.5), 1j) + self.assertAlmostEqual(pow(-1, 1./3), 0.5 + 0.8660254037844386j) + self.assertRaises(TypeError, pow, -1, -2, 3) self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(TypeError, pow, -1L, -2L, 3L) self.assertRaises(ValueError, pow, 1L, 2L, 0L) - self.assertRaises(ValueError, pow, -342.43, 0.234) self.assertRaises(TypeError, pow) @@ -1622,6 +1624,7 @@ def test_round(self): self.assertEqual(round(0.0), 0.0) + self.assertEqual(type(round(0.0)), long) self.assertEqual(round(1.0), 1.0) self.assertEqual(round(10.0), 10.0) self.assertEqual(round(1000000000.0), 1000000000.0) @@ -1650,12 +1653,50 @@ self.assertEqual(round(-999999999.9), -1000000000.0) self.assertEqual(round(-8.0, -1), -10.0) + self.assertEqual(type(round(-8.0, -1)), float) + self.assertEqual(type(round(-8.0, 0)), float) + self.assertEqual(type(round(-8.0, 1)), float) + + # Check even / odd rounding behaviour + self.assertEqual(round(5.5), 6) + self.assertEqual(round(6.5), 6) + self.assertEqual(round(-5.5), -6) + self.assertEqual(round(-6.5), -6) + + # Check behavior on ints + self.assertEqual(round(0), 0) + self.assertEqual(round(8), 8) + self.assertEqual(round(-8), -8) + self.assertEqual(type(round(0)), int) + self.assertEqual(type(round(-8, -1)), float) + self.assertEqual(type(round(-8, 0)), float) + self.assertEqual(type(round(-8, 1)), float) + # test new kwargs self.assertEqual(round(number=-8.0, ndigits=-1), -10.0) self.assertRaises(TypeError, round) + # test generic rounding delegation for reals + class TestRound(object): + def __round__(self): + return 23 + + class TestNoRound(object): + pass + + self.assertEqual(round(TestRound()), 23) + + self.assertRaises(TypeError, round, 1, 2, 3) + # XXX: This is not ideal, but see the comment in builtin_round(). + self.assertRaises(AttributeError, round, TestNoRound()) + + t = TestNoRound() + t.__round__ = lambda *args: args + self.assertEquals((), round(t)) + self.assertEquals((0,), round(t, 0)) + def test_setattr(self): setattr(sys, 'spam', 1) self.assertEqual(sys.spam, 1) @@ -1697,6 +1738,38 @@ raise ValueError self.assertRaises(ValueError, sum, BadSeq()) + def test_trunc(self): + + self.assertEqual(trunc(1), 1) + self.assertEqual(trunc(-1), -1) + self.assertEqual(type(trunc(1)), int) + self.assertEqual(type(trunc(1.5)), int) + self.assertEqual(trunc(1.5), 1) + self.assertEqual(trunc(-1.5), -1) + self.assertEqual(trunc(1.999999), 1) + self.assertEqual(trunc(-1.999999), -1) + self.assertEqual(trunc(-0.999999), -0) + self.assertEqual(trunc(-100.999), -100) + + class TestTrunc(object): + def __trunc__(self): + return 23 + + class TestNoTrunc(object): + pass + + self.assertEqual(trunc(TestTrunc()), 23) + + self.assertRaises(TypeError, trunc) + self.assertRaises(TypeError, trunc, 1, 2) + # XXX: This is not ideal, but see the comment in builtin_trunc(). + self.assertRaises(AttributeError, trunc, TestNoTrunc()) + + t = TestNoTrunc() + t.__trunc__ = lambda *args: args + self.assertEquals((), trunc(t)) + self.assertRaises(TypeError, trunc, t, 0) + def test_tuple(self): self.assertEqual(tuple(()), ()) t0_3 = (0, 1, 2, 3) Index: Modules/mathmodule.c =================================================================== --- Modules/mathmodule.c (revision 59590) +++ Modules/mathmodule.c (working copy) @@ -107,9 +107,28 @@ FUNC2(atan2, atan2, "atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n" "Unlike atan(y/x), the signs of both x and y are considered.") -FUNC1(ceil, ceil, - "ceil(x)\n\nReturn the ceiling of x as a float.\n" - "This is the smallest integral value >= x.") + +static PyObject * math_ceil(PyObject *self, PyObject *number) { + static PyObject *ceil_str = NULL; + PyObject *method; + + if (ceil_str == NULL) { + ceil_str = PyString_FromString("__ceil__"); + if (ceil_str == NULL) + return NULL; + } + + method = _PyType_Lookup(Py_Type(number), ceil_str); + if (method == NULL) + return math_1(number, ceil); + else + return PyObject_CallFunction(method, "O", number); +} + +PyDoc_STRVAR(math_ceil_doc, + "ceil(x)\n\nReturn the ceiling of x as a float.\n" + "This is the smallest integral value >= x."); + FUNC1(cos, cos, "cos(x)\n\nReturn the cosine of x (measured in radians).") FUNC1(cosh, cosh, @@ -118,9 +137,28 @@ "exp(x)\n\nReturn e raised to the power of x.") FUNC1(fabs, fabs, "fabs(x)\n\nReturn the absolute value of the float x.") -FUNC1(floor, floor, - "floor(x)\n\nReturn the floor of x as a float.\n" - "This is the largest integral value <= x.") + +static PyObject * math_floor(PyObject *self, PyObject *number) { + static PyObject *floor_str = NULL; + PyObject *method; + + if (floor_str == NULL) { + floor_str = PyString_FromString("__floor__"); + if (floor_str == NULL) + return NULL; + } + + method = _PyType_Lookup(Py_Type(number), floor_str); + if (method == NULL) + return math_1(number, floor); + else + return PyObject_CallFunction(method, "O", number); +} + +PyDoc_STRVAR(math_floor_doc, + "floor(x)\n\nReturn the floor of x as a float.\n" + "This is the largest integral value <= x."); + FUNC2(fmod, fmod, "fmod(x,y)\n\nReturn fmod(x, y), according to platform C." " x % y may differ.")