Commits

Anonymous committed a7dc31e

* added the vetor_elementwiseproxy which lets you apply calculations on
each element. This also supports __pow__, __abs__ and __mod__
* added tests for the above

  • Participants
  • Parent commits 0959bd7
  • Branches math_module

Comments (0)

Files changed (2)

 #define SWIZZLE_ERR_EXTRACTION_ERR 2
 
 static PyTypeObject PyVector2_Type;
+static PyTypeObject PyVectorElementwiseProxy_Type;
 #define PyVector2_Check(x) ((x)->ob_type == &PyVector2_Type)
 #define PyVector_Check(x) (PyVector2_Check(x))
+#define vector_elementwiseproxy_Check(x) \
+    ((x)->ob_type == &PyVectorElementwiseProxy_Type)
 
 #define DEG2RAD(angle) ((angle) * M_PI / 180.)
 #define RAD2DEG(angle) ((angle) * 180. / M_PI)
 vector_richcompare(PyVector *self, PyObject *other, int op)
 {
     int i;
+    double diff;
     if (!checkPyVectorCompatible(other, self->dim)) {
         if (op == Py_EQ)
             Py_RETURN_FALSE;
     switch(op) {
     case Py_EQ:
         for (i = 0; i < self->dim; i++) {
-            if (fabs(self->coords[i] - PySequence_GetItem_AsDouble(other, i)) >= self->epsilon) {
+            diff = self->coords[i] - PySequence_GetItem_AsDouble(other, i);
+            // test diff != diff to catch NaN
+            if ((diff != diff) || (fabs(diff) >= self->epsilon)) {
                 Py_RETURN_FALSE;
             }
         }
         break;
     case Py_NE:
         for (i = 0; i < self->dim; i++) {
-            if (fabs(self->coords[i] - PySequence_GetItem_AsDouble(other, i)) >= self->epsilon) {
+            diff = self->coords[i] - PySequence_GetItem_AsDouble(other, i);
+            if ((diff != diff) || (fabs(diff) >= self->epsilon)) {
                 Py_RETURN_TRUE;
             }
         }
 }
 
 
+static PyObject *vector_elementwise(PyVector *self);
 
 static PyMethodDef vector2_methods[] = {
     {"enable_swizzle", (PyCFunction)vector_enable_swizzle, METH_NOARGS,
     {"distance_squared_to", (PyCFunction)vector_distance_squared_to, METH_O,
      "returns the squared distance to the given vector."
     },
+    {"elementwise", (PyCFunction)vector_elementwise, METH_NOARGS,
+     "applies the following operation to each element of the vector."
+    },
     
     {NULL}  /* Sentinel */
 };
 static PyObject*
 vector_getAttr_swizzle(PyVector *self, PyObject *attr_name)
 {
-    PyObject *res = PyObject_GenericGetAttr(self, attr_name);
+    PyObject *res = PyObject_GenericGetAttr((PyObject*)self, attr_name);
     /* if normal lookup failed try to swizzle */
     if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) {
         Py_ssize_t len = PySequence_Length(attr_name);
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef struct {
+    PyObject_HEAD
+    PyVector *vec;
+} vector_elementwiseproxy;
+
+static void
+vector_elementwiseproxy_dealloc(vector_elementwiseproxy *it)
+{
+    Py_XDECREF(it->vec);
+    PyObject_Del(it);
+}
+    
+
+
+
+static PyObject *
+vector_elementwiseproxy_richcompare(PyObject *o1, PyObject *o2, int op)
+{
+    int i, dim;
+    double value, diff;
+    PyVector *vec;
+    if (vector_elementwiseproxy_Check(o1)) {
+        vec = (PyVector*)((vector_elementwiseproxy*)o1)->vec;
+        dim = vec->dim;
+        // use diff == diff to check for NaN;
+        // TODO: how should NaN be handled with LT/LE/GT/GE?
+        if (checkPyVectorCompatible(o2, dim)) {
+            switch (op) {
+            case Py_EQ:
+                for (i = 0; i < dim; i++) {
+                    diff = vec->coords[i] - PySequence_GetItem_AsDouble(o2, i);
+                    if ((diff != diff) || (fabs(diff) >= vec->epsilon)) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_NE:
+                for (i = 0; i < dim; i++) {
+                    diff = vec->coords[i] - PySequence_GetItem_AsDouble(o2, i);
+                    if ((diff == diff) && (fabs(diff) < vec->epsilon)) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_LT:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] >= PySequence_GetItem_AsDouble(o2, i))
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_LE:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] > PySequence_GetItem_AsDouble(o2, i))
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GT:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] <= PySequence_GetItem_AsDouble(o2, i))
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GE:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] < PySequence_GetItem_AsDouble(o2, i))
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            default:
+                Py_INCREF(Py_NotImplemented);
+                return Py_NotImplemented;
+            }
+        }
+        else if (RealNumber_Check(o2)) {
+            value = PyFloat_AsDouble(o2);
+            switch (op) {
+            case Py_EQ:
+                for (i = 0; i < dim; i++) {
+                    diff = vec->coords[i] - value;
+                    if (diff != diff || fabs(diff) >= vec->epsilon) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_NE:
+                for (i = 0; i < dim; i++) {
+                    diff = vec->coords[i] - value;
+                    if (diff == diff && fabs(diff) < vec->epsilon) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_LT:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] >= value) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_LE:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] > value) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GT:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] <= value) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GE:
+                for (i = 0; i < dim; i++) {
+                    if (vec->coords[i] < value) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            default:
+                Py_INCREF(Py_NotImplemented);
+                return Py_NotImplemented;
+            }
+                
+        }
+    }
+    else {
+        vec = (PyVector*)((vector_elementwiseproxy*)o2)->vec;
+        dim = vec->dim;
+        if (checkPyVectorCompatible(o1, dim)) {
+            switch (op) {
+            case Py_EQ:
+                for (i = 0; i < dim; i++) {
+                    diff = PySequence_GetItem_AsDouble(o1, i) - vec->coords[i];
+                    if (diff != diff || fabs(diff) >= vec->epsilon) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_NE:
+                for (i = 0; i < dim; i++) {
+                    diff = PySequence_GetItem_AsDouble(o1, i) - vec->coords[i];
+                    if (diff == diff && fabs(diff) < vec->epsilon) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_LT:
+                for (i = 0; i < dim; i++) {
+                    if (PySequence_GetItem_AsDouble(o1, i) >= vec->coords[i])
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_LE:
+                for (i = 0; i < dim; i++) {
+                    if (PySequence_GetItem_AsDouble(o1, i) > vec->coords[i])
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GT:
+                for (i = 0; i < dim; i++) {
+                    if (PySequence_GetItem_AsDouble(o1, i) <= vec->coords[i])
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GE:
+                for (i = 0; i < dim; i++) {
+                    if (PySequence_GetItem_AsDouble(o1, i) < vec->coords[i])
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            default:
+                Py_INCREF(Py_NotImplemented);
+                return Py_NotImplemented;
+            }
+        }
+        else if (RealNumber_Check(o1)) {
+            value = PyFloat_AsDouble(o1);
+            switch (op) {
+            case Py_EQ:
+                for (i = 0; i < dim; i++) {
+                    diff = value - vec->coords[i];
+                    if (diff != diff || fabs(diff) >= vec->epsilon) {
+                        Py_RETURN_FALSE;
+                    }
+                }
+                Py_RETURN_TRUE;
+            case Py_NE:
+                for (i = 0; i < dim; i++) {
+                    diff = value - vec->coords[i];
+                    if (diff == diff && fabs(diff) >= vec->epsilon) {
+                        Py_RETURN_TRUE;
+                    }
+                }
+                Py_RETURN_FALSE;
+            case Py_LT:
+                for (i = 0; i < dim; i++) {
+                    if (value >= vec->coords[i])
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_LE:
+                for (i = 0; i < dim; i++) {
+                    if (value > vec->coords[i]) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GT:
+                for (i = 0; i < dim; i++) {
+                    if (value <= vec->coords[i]) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            case Py_GE:
+                for (i = 0; i < dim; i++) {
+                    if (value < vec->coords[i]) 
+                        Py_RETURN_FALSE;
+                }
+                Py_RETURN_TRUE;
+            default:
+                Py_INCREF(Py_NotImplemented);
+                return Py_NotImplemented;
+            }
+        }
+    }
+}
+
+
+
+
+/**********************************************
+ * Generic vector PyNumber emulation routines
+ **********************************************/
+
+
+static PyObject *
+vector_elementwiseproxy_add(PyObject *o1, PyObject *o2)
+{
+    int i, dim;
+    double value;
+    vector_elementwiseproxy *proxy;
+    PyObject *other;
+    PyVector *ret;
+    if (vector_elementwiseproxy_Check(o1)) {
+        proxy = (vector_elementwiseproxy*)o1;
+        other = o2;
+    }
+    else {
+        other = o1;
+        proxy = (vector_elementwiseproxy*)o2;
+    }
+    dim = ((PyVector*)proxy->vec)->dim;
+    if (checkPyVectorCompatible(other, dim)) {
+        return vector_add((PyObject*)proxy->vec, other);
+    }
+    else if (RealNumber_Check(other)) {
+        value = PyFloat_AsDouble(other);
+        ret = (PyVector*)PyVector_NEW(dim);
+        for (i = 0; i < dim; i++) {
+            ret->coords[i] = proxy->vec->coords[i] + value;
+        }
+        return (PyObject*)ret;
+    }
+
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+
+static PyObject *
+vector_elementwiseproxy_sub(PyObject *o1, PyObject *o2)
+{
+    int i, dim;
+    double value;
+    PyVector *ret;
+    if (vector_elementwiseproxy_Check(o1)) {
+        dim = ((PyVector*)((vector_elementwiseproxy*)o1)->vec)->dim;
+        if (checkPyVectorCompatible(o2, dim)) {
+            return vector_sub((PyObject*)((vector_elementwiseproxy*)o1)->vec, o2);
+        }
+        else if (RealNumber_Check(o2)) {
+            value = PyFloat_AsDouble(o2);
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = ((PyVector*)((vector_elementwiseproxy*)o1)->vec)->coords[i] - value;
+            }
+            return (PyObject*)ret;
+        }
+    }
+    else {
+        dim = ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->dim;
+        if (checkPyVectorCompatible(o1, dim)) {
+            return vector_sub(o1, (PyObject*)((vector_elementwiseproxy*)o2)->vec);
+        }
+        else if (RealNumber_Check(o1)) {
+            value = PyFloat_AsDouble(o1);
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = value - ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->coords[i];
+            }
+            return (PyObject*)ret;
+        }
+    }
+
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+
+static PyObject *
+vector_elementwiseproxy_mul(PyObject *o1, PyObject *o2)
+{
+    int i, row, col, dim;
+    PyVector *ret;
+    vector_elementwiseproxy *proxy;
+    PyObject *other;
+    if (vector_elementwiseproxy_Check(o1)) {
+        proxy = (vector_elementwiseproxy*)o1;
+        other = o2;
+    }
+    else {
+        other = o1;
+        proxy = (vector_elementwiseproxy*)o2;
+    }
+    dim = ((PyVector*)proxy->vec)->dim;
+    // elementwiseproxy * vector ?
+    if (checkPyVectorCompatible(other, dim)) {
+        ret = (PyVector*)PyVector_NEW(dim);
+        for (i = 0; i < dim; i++) {
+            ret->coords[i] = (((PyVector*)proxy->vec)->coords[i] * 
+                              PySequence_GetItem_AsDouble(other, i));
+        }
+        return (PyObject*)ret;
+    }
+    // elementwise * scalar ?
+    else if (RealNumber_Check(other)) {
+        return vector_mul((PyObject*)proxy->vec, other);
+    }
+
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+
+static PyObject *
+vector_elementwiseproxy_div(PyObject *o1, PyObject *o2)
+{
+    int i, dim;
+    double value;
+    PyVector *ret;
+    if (vector_elementwiseproxy_Check(o1)) {
+        dim = ((PyVector*)((vector_elementwiseproxy*)o1)->vec)->dim;
+        if (checkPyVectorCompatible(o2, dim)) {
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = ((PyVector*)((vector_elementwiseproxy*)o1)->vec)->coords[i] / PySequence_GetItem_AsDouble(o2, i);
+            }
+            return (PyObject*)ret;
+        }
+        else if (RealNumber_Check(o2)) {
+            return vector_div(((vector_elementwiseproxy*)o1)->vec, o2);
+        }
+    }
+    else {
+        dim = ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->dim;
+        if (checkPyVectorCompatible(o1, dim)) {
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = PySequence_GetItem_AsDouble(o1, i) / ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->coords[i];
+            }
+            return (PyObject*)ret;
+        }
+        else if (RealNumber_Check(o1)) {
+            value = PyFloat_AsDouble(o1);
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = value / ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->coords[i];
+            }
+            return (PyObject*)ret;
+        }
+    }
+
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+static PyObject *
+vector_elementwiseproxy_floor_div(PyObject *o1, PyObject *o2)
+{
+    int i, dim;
+    double value;
+    PyVector *ret;
+    if (vector_elementwiseproxy_Check(o1)) {
+        dim = ((PyVector*)((vector_elementwiseproxy*)o1)->vec)->dim;
+        if (checkPyVectorCompatible(o2, dim)) {
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = floor(((PyVector*)((vector_elementwiseproxy*)o1)->vec)->coords[i] / PySequence_GetItem_AsDouble(o2, i));
+            }
+            return (PyObject*)ret;
+        }
+        else if (RealNumber_Check(o2)) {
+            return vector_floor_div(((vector_elementwiseproxy*)o1)->vec, o2);
+        }
+    }
+    else {
+        dim = ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->dim;
+        if (checkPyVectorCompatible(o1, dim)) {
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = floor(PySequence_GetItem_AsDouble(o1, i) / ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->coords[i]);
+            }
+            return (PyObject*)ret;
+        }
+        else if (RealNumber_Check(o1)) {
+            value = PyFloat_AsDouble(o1);
+            ret = (PyVector*)PyVector_NEW(dim);
+            for (i = 0; i < dim; i++) {
+                ret->coords[i] = floor(value / ((PyVector*)((vector_elementwiseproxy*)o2)->vec)->coords[i]);
+            }
+            return (PyObject*)ret;
+        }
+    }
+
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+static PyObject *
+vector_elementwiseproxy_pow(vector_elementwiseproxy *self, PyObject *expoObj, 
+                            PyObject *modulo)
+{
+    int i;
+    double expo;
+    PyVector *ret;
+    if (modulo != Py_None) {
+        PyErr_SetString(PyExc_SystemError, "__pow__ with third argument was not implemented for vectors, yet.");
+        return NULL;
+    }
+    if (!RealNumber_Check(expoObj)) {
+        PyErr_SetString(PyExc_TypeError, "expected a float as second argument.");
+        return NULL;
+    }
+    /* if exponent is fractional all entries must be positive */
+    if (!PyInt_Check(expoObj)) {
+        for (i = 0; i < self->vec->dim; i++) {
+            if (self->vec->coords[i] < 0) {
+                PyErr_SetString(PyExc_ValueError, "negative numbers cannot be raised to a fractional power");
+                return NULL;
+            }
+        }
+    }
+    expo = PyFloat_AsDouble(expoObj);
+    ret = (PyVector*)PyVector_NEW(self->vec->dim);
+    for (i = 0; i < self->vec->dim; i++) {
+        ret->coords[i] = pow(self->vec->coords[i], expo);
+    }
+    return (PyObject*)ret;
+}
+
+static PyObject *
+vector_elementwiseproxy_mod(PyObject *o1, PyObject *o2)
+{
+    /* TODO: There wasn't put much thought behind this implementation and
+             it is late so this should probably be thoughly reviewed. */
+    int i;
+    PyObject *entry, *divisor, *result;
+    PyVector *res, *vec;
+    if (vector_elementwiseproxy_Check(o1)) {
+        vec = ((vector_elementwiseproxy*)o1)->vec;
+        if (checkPyVectorCompatible(o2, vec->dim)) { 
+            res = (PyVector*)PyVector_NEW(vec->dim);
+            for (i = 0; i < vec->dim; i++) {
+                entry = PyFloat_FromDouble(vec->coords[i]);
+                divisor = PyFloat_FromDouble(PySequence_GetItem_AsDouble(o2, i));
+                result = PyNumber_Remainder(entry, divisor);
+                res->coords[i] = PyFloat_AsDouble(result);
+                Py_DECREF(entry);
+                Py_DECREF(divisor);
+                Py_DECREF(result);
+            }
+            return (PyObject*)res;
+        }
+        else if (RealNumber_Check(o2)) {
+            res = (PyVector*)PyVector_NEW(vec->dim);
+            for (i = 0; i < vec->dim; i++) {
+                entry = PyFloat_FromDouble(vec->coords[i]);
+                result = PyNumber_Remainder(entry, o2);
+                res->coords[i] = PyFloat_AsDouble(result);
+                Py_DECREF(entry);
+                Py_DECREF(result);
+            }
+            return (PyObject*)res;
+        }
+    }
+    else {
+        vec = ((vector_elementwiseproxy*)o2)->vec;
+        if (checkPyVectorCompatible(o1, vec->dim)) { 
+            res = (PyVector*)PyVector_NEW(vec->dim);
+            for (i = 0; i < vec->dim; i++) {
+                divisor = PyFloat_FromDouble(vec->coords[i]);
+                entry = PyFloat_FromDouble(PySequence_GetItem_AsDouble(o1, i));
+                result = PyNumber_Remainder(entry, divisor);
+                res->coords[i] = PyFloat_AsDouble(result);
+                Py_DECREF(entry);
+                Py_DECREF(divisor);
+                Py_DECREF(result);
+            }
+            return (PyObject*)res;
+        }
+        else if (RealNumber_Check(o1)) {
+            res = (PyVector*)PyVector_NEW(vec->dim);
+            for (i = 0; i < vec->dim; i++) {
+                divisor = PyFloat_FromDouble(vec->coords[i]);
+                result = PyNumber_Remainder(o1, divisor);
+                res->coords[i] = PyFloat_AsDouble(result);
+                Py_DECREF(divisor);
+                Py_DECREF(result);
+            }
+            return (PyObject*)res;
+        }
+    }
+
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+}
+
+
+static PyObject *
+vector_elementwiseproxy_abs(vector_elementwiseproxy *self)
+{
+    int i;
+    PyVector *ret = (PyVector*)PyVector_NEW(self->vec->dim);
+    for (i = 0; i < self->vec->dim; i++) {
+        ret->coords[i] = fabs(self->vec->coords[i]);
+    }
+    return (PyObject*)ret;
+}
+
+static PyObject *
+vector_elementwiseproxy_neg(vector_elementwiseproxy *self)
+{
+    int i;
+    PyVector *ret = (PyVector*)PyVector_NEW(self->vec->dim);
+    for (i = 0; i < self->vec->dim; i++) {
+        ret->coords[i] = -self->vec->coords[i];
+    }
+    return (PyObject*)ret;
+}
+
+static PyObject *
+vector_elementwiseproxy_pos(vector_elementwiseproxy *self)
+{
+    int i;
+    PyVector *ret = (PyVector*)PyVector_NEW(self->vec->dim);
+    memcpy(ret->coords, self->vec->coords, sizeof(ret->coords[0]) * ret->dim);
+    return (PyObject*)ret;
+}
+
+static int
+vector_elementwiseproxy_nonzero(vector_elementwiseproxy *self)
+{
+    int i;
+    for (i = 0; i < self->vec->dim; i++) {
+        if (fabs(self->vec->coords[i]) > self->vec->epsilon) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+PyNumberMethods vector_elementwiseproxy_as_number = {
+    (binaryfunc)vector_elementwiseproxy_add,      /* nb_add;       __add__ */
+    (binaryfunc)vector_elementwiseproxy_sub,      /* nb_subtract;  __sub__ */
+    (binaryfunc)vector_elementwiseproxy_mul,      /* nb_multiply;  __mul__ */
+    (binaryfunc)vector_elementwiseproxy_div,      /* nb_divide;    __div__ */
+    (binaryfunc)vector_elementwiseproxy_mod,      /* nb_remainder; __mod__ */
+    (binaryfunc)0,                                /* nb_divmod;    __divmod__ */
+    (ternaryfunc)vector_elementwiseproxy_pow,     /* nb_power;     __pow__ */
+    (unaryfunc)vector_elementwiseproxy_neg, /* nb_negative;  __neg__ */
+    (unaryfunc)vector_elementwiseproxy_pos, /* nb_positive;  __pos__ */
+    (unaryfunc)vector_elementwiseproxy_abs, /* nb_absolute;  __abs__ */
+    (inquiry)vector_elementwiseproxy_nonzero, /* nb_nonzero;   __nonzero__ */
+    (unaryfunc)0,                   /* nb_invert;    __invert__ */
+    (binaryfunc)0,                  /* nb_lshift;    __lshift__ */
+    (binaryfunc)0,                  /* nb_rshift;    __rshift__ */
+    (binaryfunc)0,                  /* nb_and;       __and__ */
+    (binaryfunc)0,                  /* nb_xor;       __xor__ */
+    (binaryfunc)0,                  /* nb_or;        __or__ */
+    (coercion)0,                    /* nb_coerce;    __coerce__ */
+    (unaryfunc)0,                   /* nb_int;       __int__ */
+    (unaryfunc)0,                   /* nb_long;      __long__ */
+    (unaryfunc)0,                   /* nb_float;     __float__ */
+    (unaryfunc)0,                   /* nb_oct;       __oct__ */
+    (unaryfunc)0,                   /* nb_hex;       __hex__ */
+
+    /* Added in release 2.0 */
+    (binaryfunc)0,                  /* nb_inplace_add;       __iadd__ */
+    (binaryfunc)0,                  /* nb_inplace_subtract;  __isub__ */
+    (binaryfunc)0,                  /* nb_inplace_multiply;  __imul__ */
+    (binaryfunc)0,                  /* nb_inplace_divide;    __idiv__ */
+    (binaryfunc)0,                  /* nb_inplace_remainder; __imod__ */
+    (ternaryfunc)0,                 /* nb_inplace_power;     __pow__ */
+    (binaryfunc)0,                  /* nb_inplace_lshift;    __ilshift__ */
+    (binaryfunc)0,                  /* nb_inplace_rshift;    __irshift__ */
+    (binaryfunc)0,                  /* nb_inplace_and;       __iand__ */
+    (binaryfunc)0,                  /* nb_inplace_xor;       __ixor__ */
+    (binaryfunc)0,                  /* nb_inplace_or;        __ior__ */
+
+    /* Added in release 2.2 */
+    (binaryfunc)vector_elementwiseproxy_floor_div, /* nb_floor_divide;         __floor__ */
+    (binaryfunc)vector_elementwiseproxy_div, /* nb_true_divide;          __truediv__ */
+    (binaryfunc)0,                  /* nb_inplace_floor_divide; __ifloor__ */
+    (binaryfunc)0,                  /* nb_inplace_true_divide;  __itruediv__ */
+
+    /* Added in release 2.5 */
+    (unaryfunc)0,                   /* nb_index;  __index__ */
+};
+
+
+
+static PyTypeObject PyVectorElementwiseProxy_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /* ob_size */
+    "pygame.math.VectorElementwiseProxy", /* tp_name */
+    sizeof(vector_elementwiseproxy), /* tp_basicsize */
+    0,                         /* tp_itemsize */
+    /* Methods to implement standard operations */
+    (destructor)vector_elementwiseproxy_dealloc, /* tp_dealloc */
+    0,                         /* tp_print */
+    0,                         /* tp_getattr */
+    0,                         /* tp_setattr */
+    0,                         /* tp_compare */
+    (reprfunc)0,               /* tp_repr */
+    /* Method suites for standard classes */
+    &vector_elementwiseproxy_as_number,         /* tp_as_number */
+    0,                         /* tp_as_sequence */
+    0,                         /* tp_as_mapping */
+    /* More standard operations (here for binary compatibility) */
+    0,                         /* tp_hash */
+    0,                         /* tp_call */
+    (reprfunc)0,               /* tp_str */
+    (getattrofunc)0,           /* tp_getattro */
+    (setattrofunc)0,           /* tp_setattro */
+    /* Functions to access object as input/output buffer */
+    0,                         /* tp_as_buffer */
+    /* Flags to define presence of optional/expanded features */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
+    /* Documentation string */
+    0,                         /* tp_doc */
+
+    /* Assigned meaning in release 2.0 */
+    /* call function for all accessible objects */
+    0,                         /* tp_traverse */
+    /* delete references to contained objects */
+    0,                         /* tp_clear */
+
+    /* Assigned meaning in release 2.1 */
+    /* rich comparisons */
+    (richcmpfunc)vector_elementwiseproxy_richcompare, /* tp_richcompare */
+    /* weak reference enabler */
+    0,                         /* tp_weaklistoffset */
+
+    /* Added in release 2.2 */
+    /* Iterators */
+    0,                         /* tp_iter */
+    0,                         /* tp_iternext */
+    /* Attribute descriptor and subclassing stuff */
+    0,                         /* 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 */
+    (initproc)0,               /* tp_init */
+    0,                         /* tp_alloc */
+    (newfunc)0,                /* tp_new */
+    0,                         /* tp_free */
+    0,                         /* tp_is_gc */
+    0,                         /* tp_bases */
+    0,                         /* tp_mro */
+    0,                         /* tp_cache */
+    0,                         /* tp_subclasses */
+    0,                         /* tp_weaklist */
+};
+
+
+
+static PyObject *
+vector_elementwise(PyVector *vec)
+{
+    vector_elementwiseproxy *proxy;
+    if (!PyVector_Check(vec)) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+
+    proxy = PyObject_New(vector_elementwiseproxy, 
+                         &PyVectorElementwiseProxy_Type);
+    if (proxy == NULL)
+        return NULL;
+    Py_INCREF(vec);
+    proxy->vec = (PyVector *)vec;
+    return (PyObject *)proxy;
+}
+
+
+
+
+
+
+
+
+
+
 static PyMethodDef _math_methods[] =
 {
     {NULL, NULL, 0, NULL}

test/math_test.py

                          diff.x * diff.x + diff.y * diff.y)
         self.assertEqual(v1.distance_squared_to(v1), 0)
         self.assertEqual(v1.distance_squared_to(v2), v2.distance_squared_to(v1))
-                         
+        
     def testSwizzle(self):
         v1 = Vector2(1, 2)
         self.assertEquals(hasattr(v1, "enable_swizzle"), True)
             Vector2().xy = 3
         self.assertRaises(TypeError, invalidAssignment)
 
-        
+    def test_elementwise(self):
+        v1 = Vector2(2, -7)
+        v2 = Vector2(1.5, 37)
+        # behaviour for "elementwise op scalar"
+        v1.elementwise()
+        self.assertEqual(type(v1.elementwise() * 3.141), type(v1))
+        self.assertEqual(v1.elementwise() + 5, v1 + Vector2(5, 5))
+        self.assertEqual(v1.elementwise() - 3.5, v1 - Vector2(3.5, 3.5))
+        self.assertEqual(v1.elementwise() * 7.5, v1 * 7.5)
+        self.assertEqual(v1.elementwise() / -3.5, v1 / -3.5)
+        self.assertEqual(v1.elementwise() // -3.5, v1 // -3.5)
+        self.assertEqual(v1.elementwise() % 3, (v1.x % 3, v1.y % 3))
+        self.assertEqual(v1.elementwise() > 2, v1.x > 2 and v1.y > 2)
+        self.assertEqual(v1.elementwise() < 2, v1.x < 2 and v1.y < 2)
+        self.assertEqual(v1.elementwise() == 1, v1.x == 1 and v1.y == 1)
+        self.assertEqual(v1.elementwise() != 1, v1.x != 1 and v1.y != 1)
+        self.assertEqual(v1.elementwise() >= 2, v1.x >= 2 and v1.y >= 2)
+        self.assertEqual(v1.elementwise() <= -7, v1.x <= -7 and v1.y <= -7)
+        self.assertEqual(v1.elementwise() != -7, v1.x != -7 and v1.y != -7)
+        # behaviour for "scalar op elementwise"
+        self.assertEqual(5 + v1.elementwise(), Vector2(5, 5) + v1)
+        self.assertEqual(3.5 - v1.elementwise(), Vector2(3.5, 3.5) - v1)
+        self.assertEqual(7.5 * v1.elementwise() , 7.5 * v1)
+        self.assertEqual(-3.5 / v1.elementwise(), (-3.5 / v1.x, -3.5 / v1.y))
+        self.assertEqual(-3.5 // v1.elementwise(), (-3.5 // v1.x, -3.5 // v1.y))
+        self.assertEqual(3 % v1.elementwise(), (3 % v1.x, 3 % v1.y))
+        self.assertEqual(2 < v1.elementwise(), 2 < v1.x and 2 < v1.y)
+        self.assertEqual(2 > v1.elementwise(), 2 > v1.x and 2 > v1.y)
+        self.assertEqual(1 == v1.elementwise(), 1 == v1.x and 1 == v1.y)
+        self.assertEqual(1 != v1.elementwise(), 1 != v1.x and 1 != v1.y)
+        self.assertEqual(2 <= v1.elementwise(), 2 <= v1.x and 2 <= v1.y)
+        self.assertEqual(-7 >= v1.elementwise(), -7 >= v1.x and -7 >= v1.y)
+        self.assertEqual(-7 != v1.elementwise(), -7 != v1.x and -7 != v1.y)
+
+        # behaviour for "elementwise op vector"
+        self.assertEqual(type(v1.elementwise() * v2), type(v1))
+        self.assertEqual(v1.elementwise() + v2, v1 + v2)
+        self.assertEqual(v1.elementwise() - v2, v1 - v2)
+        self.assertEqual(v1.elementwise() * v2, (v1.x * v2.x, v1.y * v2.y))
+        self.assertEqual(v1.elementwise() / v2, (v1.x / v2.x, v1.y / v2.y))
+        self.assertEqual(v1.elementwise() // v2, (v1.x // v2.x, v1.y // v2.y))
+        self.assertEqual(v1.elementwise() % v2, (v1.x % v2.x, v1.y % v2.y))
+        self.assertEqual(v1.elementwise() > v2, v1.x > v2.x and v1.y > v2.y)
+        self.assertEqual(v1.elementwise() < v2, v1.x < v2.x and v1.y < v2.y)
+        self.assertEqual(v1.elementwise() >= v2, v1.x >= v2.x and v1.y >= v2.y)
+        self.assertEqual(v1.elementwise() <= v2, v1.x <= v2.x and v1.y <= v2.y)
+        self.assertEqual(v1.elementwise() == v2, v1.x == v2.x and v1.y == v2.y)
+        self.assertEqual(v1.elementwise() != v2, v1.x != v2.x and v1.y != v2.y)
+        # behaviour for "vector op elementwise"
+        self.assertEqual(v2 + v1.elementwise(), v2 + v1)
+        self.assertEqual(v2 - v1.elementwise(), v2 - v1)
+        self.assertEqual(v2 * v1.elementwise(), (v2.x * v1.x, v2.y * v1.y))
+        self.assertEqual(v2 / v1.elementwise(), (v2.x / v1.x, v2.y / v1.y))
+        self.assertEqual(v2 // v1.elementwise(), (v2.x // v1.x, v2.y // v1.y))
+        self.assertEqual(v2 % v1.elementwise(), (v2.x % v1.x, v2.y % v1.y))
+        self.assertEqual(v2 < v1.elementwise(), v2.x < v1.x and v2.y < v1.y)
+        self.assertEqual(v2 > v1.elementwise(), v2.x > v1.x and v2.y > v1.y)
+        self.assertEqual(v2 <= v1.elementwise(), v2.x <= v1.x and v2.y <= v1.y)
+        self.assertEqual(v2 >= v1.elementwise(), v2.x >= v1.x and v2.y >= v1.y)
+        self.assertEqual(v2 == v1.elementwise(), v2.x == v1.x and v2.y == v1.y)
+        self.assertEqual(v2 != v1.elementwise(), v2.x != v1.x and v2.y != v1.y)
+
+        # other behaviour
+        self.assertEqual(abs(v1.elementwise()), Vector2(abs(v1.x), abs(v1.y)))
+        self.assertEqual(-v1.elementwise(), -v1)
+        self.assertEqual(+v1.elementwise(), +v1)
+        self.assertEqual(bool(v1.elementwise()), bool(v1))
+        self.assertEqual(bool(Vector2().elementwise()), bool(Vector2()))
+        expo = -3
+        self.assertEqual(pow(v1.elementwise(), expo), list(pow(entry, expo) for entry in v1))
+        expo = 1.2
+        self.assertRaises(ValueError, lambda : pow(v1.elementwise(), expo))
+
+
 
 if __name__ == '__main__':
     unittest.main()