static void Morris_dealloc(Morris *self) { free(self->X); Py_TYPE(self)->tp_free((PyObject *) self); } static PyObject * Morris_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { // initialize attributes to 0 Morris *self; self = (Morris *) type->tp_alloc(type, 0); self->epsilon = self->delta = 0; return (PyObject *) self; } static float byte_to_float(byte a){ float ret; ret = a / 100.00 ; return ret; } static byte float_to_byte(float a){ byte ret; ret = a * 100; return ret; } static int Morris_init_helper(Morris *self, uint64_t size, byte version){ if( (self->X = (byte*) calloc(size , sizeof(byte))) == NULL){ PyErr_SetString(MemoryError, "Unable to allocate space, terminating"); return -1; } self->version_flag = version; return 0; } static int Morris_init(Morris *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"version", "epsilon", "delta", NULL}; const char* v = "Morris"; PyObject *version = Py_BuildValue("s#", v, strlen(v));; uint32_t t,s; float epsilon = 0, delta = 0; // passing in arguments if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Off", kwlist, &version, &epsilon, &delta)){ return -1; } if (!PyUnicode_Check(version)) { PyErr_SetString(PyExc_TypeError, "The version attribute must be a string"); return -1; } const char* passed_in_vesion = PyUnicode_DATA(version); // if we only have to track one stream if(strcmp(passed_in_vesion, "Morris") == 0){ return Morris_init_helper(self, 1, 0); } if((epsilon >= 1) || (epsilon <= 0) ){ PyErr_SetString(HyperParameterError, "epsilon must set between 0 and 1"); return -1; } // error check for delta val if((delta >= 1) || (delta <= 0)){ PyErr_SetString(HyperParameterError, "delta must be set between 0 and 1"); return -1; } self->epsilon = float_to_byte(epsilon); self->delta = float_to_byte(delta); if(strcmp(passed_in_vesion, "Morris+") == 0){ s = (uint32_t) ceil( 1/((float) 2 * pow(epsilon, 2) * delta)); return Morris_init_helper(self, s, 1); } if(strcmp(passed_in_vesion, "Morris++") == 0 ){ s = (uint32_t) ceil( 3/( (float) 2 * pow(epsilon, 2) )); t = (uint32_t) ceil( 18 * log(1/ (float) delta)); return Morris_init_helper(self, s*t, 2); } PyErr_SetString(VersionError, "version must be set to: Morris, Morris+, Morris++"); return -1; } static PyObject * Morris_update_helper(Morris *self, uint64_t range){ for(uint32_t i = 0; i < range; i++){ self->X[i] < log2(RAND_MAX) - log2(rand()) ? self->X[i]++ : self->X[i]; } return Py_None; } static PyObject * Morris_update(Morris *self) { // assuming range is less than or equal to 2^32 uint32_t s,t; float epsilon, delta; if(self->version_flag == 0){ return Morris_update_helper(self, 1); } epsilon = byte_to_float(self->epsilon); delta = byte_to_float(self->delta); if(self->version_flag == 1){ s = (uint32_t) ceil( 1/((float) 2 * pow(epsilon, 2) * delta)); return Morris_update_helper(self, s); } s = (uint32_t) ceil( 3/( (float) 2 * pow(epsilon, 2) )); t = (uint32_t) ceil( 18 * log(1/ (float) delta)); return Morris_update_helper(self, s*t); } // https://stackoverflow.com/questions/1787996/c-library-function-to-perform-sort static int comp (const void * elem1, const void * elem2) { int f = *((float*)elem1); int s = *((float*)elem2); if (f > s) return 1; if (f < s) return -1; return 0; } static uint64_t output_helper(uint64_t raw_count){ return (1 << raw_count) - 1; } // outputs 2^x -1 static PyObject * Morris_output(Morris *self) { float* averages; float ret; uint64_t sum = 0; uint32_t s,t; float epsilon, delta; // checking for case of no update calls if(self->X[0] == 0){ return PyLong_FromLong(0); } if(self->version_flag == 0){ return PyLong_FromLong((1 << self->X[0]) - 1); } epsilon = byte_to_float(self->epsilon); delta = byte_to_float(self->delta); if(self->version_flag == 1){ s = (uint32_t) ceil( 1/((float) 2 * pow(epsilon, 2) * delta)); for(uint32_t i = 0; i < s; i++){ sum += output_helper(self->X[i]); } ret = sum/ (double) s; // need to use doubles for as no floats in python return PyFloat_FromDouble(ret); } s = (uint32_t) ceil( 3/( (float) 2 * pow(epsilon, 2) )); t = (uint32_t) ceil( 18 * log(1/ (float) delta)); averages = (float*) calloc(t, sizeof(float)); if (averages == NULL){ // will still only use floats here to save space PyErr_SetString(MemoryError, "Unable to allocate space, terminating"); return Py_None; } for(uint32_t row = 0; row < t; row++){ for(uint32_t column = 0; column < s; column++){ sum += output_helper(self->X[(row*(s)) + column]); } averages[row] = sum/ (float) s; sum = 0; } qsort(averages,t,sizeof(float),comp); // get the median ret = (t % 2 == 0) ? ((averages[(t-1)/2] + averages[t/2])/ (float) 2.0) : averages[t/2]; free(averages); return PyFloat_FromDouble((double) ret); }