Bouncer / bouncer / counters / headers / morris_counter.h
morris_counter.h
Raw
#ifndef MORRIS_COUNTER_H
#define MORRIS_COUNTER_H

static float byte_to_float(byte); 
static byte float_to_byte(float); 

#pragma pack(1)
typedef struct {
    PyObject_HEAD
    byte* X; // counter 
    byte version_flag : 2; 
    byte epsilon : 7; 
    byte delta : 7; 
} Morris;

static void Morris_dealloc(Morris*); 
static PyObject *Morris_new(PyTypeObject*, PyObject *, PyObject *);
static int Morris_init(Morris *, PyObject *, PyObject *);
static int Morris_init_helper(Morris *, uint64_t, byte);
static PyObject *Morris_update(Morris *);
static PyObject *Morris_update_helper(Morris *, uint64_t);
static int comp(const void *, const void *);  
static uint64_t output_helper(uint64_t); 
static PyObject *Morris_output(Morris *);

static PyObject* get_version(Morris* , void* );
static PyObject* get_epsilon(Morris* , void* );
static PyObject*get_delta(Morris*, void* );
static PyObject*get_shape(Morris*, void* );
static PyObject*get_raw_counts(Morris*, void* );

static PyObject*
get_version(Morris* self, void* closure)
{
    const char* s;
    if(self->version_flag == 0){
        s = "Morris";
    }
    else if(self->version_flag == 1){
        s = "Morris+";
    }
    else{
        s = "Morris++";
    }
    return Py_BuildValue("s#", s, strlen(s));
}

static PyObject*
get_epsilon(Morris* self, void* closure)
{
    if(self->version_flag == 1 || self->version_flag == 2){
        return PyFloat_FromDouble( byte_to_float(self->epsilon) );
    }
    const char* s = "Epsilon not set for version Morris";
    return Py_BuildValue("s#", s, strlen(s));
}

static PyObject*
get_delta(Morris* self, void* closure)
{
    if(self->version_flag == 1 || self->version_flag == 2){
        return PyFloat_FromDouble(byte_to_float(self->delta) );
    }
    const char* s = "Delta not set for version Morris";
    return Py_BuildValue("s#", s, strlen(s));
}


static PyObject*
get_shape(Morris* self, void* closure){
    PyObject* shape; 
    float epsilon, delta; 
    uint32_t s,t; 

    if(self->version_flag == 0){
        if( (shape = PyTuple_Pack(1, PyLong_FromUnsignedLong((unsigned long) 1))) == NULL){
            PyErr_NoMemory();
            return Py_None;
        }
        return shape; 
    }
    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));

        if( (shape = PyTuple_Pack(2, PyLong_FromUnsignedLong((unsigned long) 1), PyLong_FromUnsignedLong((unsigned long) s))) == NULL){
            PyErr_NoMemory();
            return Py_None;
        }
        return shape; 
    }
    s  = (uint32_t) ceil( 3/( (float)  2 * pow(epsilon, 2) ));
    t = (uint32_t) ceil( 18 * log(1/ (float) delta));

    if( (shape = PyTuple_Pack(2, PyLong_FromUnsignedLong((unsigned long) t), PyLong_FromUnsignedLong((unsigned long) s))) == NULL){
        PyErr_NoMemory();
        return Py_None;
    }

    return shape; 
}

static PyObject*
get_raw_counts(Morris* self, void* closure){
    PyObject* list; 
    float epsilon, delta; 
    uint32_t s,t; 
    if(self->version_flag == 0){
        list = PyList_New(1);
        PyList_SetItem(list, 0, Py_BuildValue("i", self->X[0]));
        return list; 
    }
    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));
        list = PyList_New(s);

        for(uint32_t i = 0; i < s; i++){
            PyList_SetItem(list, i, Py_BuildValue("i", self->X[i]));
        }
        return list; 
    }

    s  = (uint32_t) ceil( 3/( (float)  2 * pow(epsilon, 2) ));
    t = (uint32_t) ceil( 18 * log(1/ (float) delta));
    list = PyList_New(t);
    for(uint32_t i = 0; i < t; i++){
        PyObject* temp = PyList_New(s);
        for(uint32_t j = 0; j < s; j++){
            PyList_SetItem(temp, j, Py_BuildValue("i", self->X[i*t + j]));
        }
        PyList_SetItem(list, i, temp);
    }
    return list; 
}

PyGetSetDef get_Morris_sets[] = {
    {"version",  /* name */
     (getter) get_version,
     NULL,
     NULL,  /* doc */
     NULL /* closure */},

     {"epsilon",  /* name */
     (getter) get_epsilon,
     NULL,
     NULL,  /* doc */
     NULL /* closure */},

     {"delta",  /* name */
     (getter) get_delta,
     NULL,
     NULL,  /* doc */
     NULL /* closure */},

     {"shape",  /* name */
     (getter) get_shape,
     NULL,
     NULL,  /* doc */
     NULL /* closure */},
     
     {"raw_counts",  /* name */
     (getter) get_raw_counts,
     NULL,
     NULL,  /* doc */
     NULL /* closure */},
    {NULL}
};

static PyMethodDef Morris_methods[] = {
    {"update", (PyCFunction) Morris_update, METH_NOARGS,
     "Probabilistically update the morris counter"
    },
    {"output", (PyCFunction) Morris_output, METH_NOARGS,
     "Output the counter estimate"
    },
    {NULL}  /* Sentinel */
};

static PyTypeObject Morris_type = {
    PyVarObject_HEAD_INIT(NULL,0)
    .tp_name = "counters.Morris",
    .tp_doc = "Morris counter",
    .tp_basicsize = sizeof(Morris),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_new = Morris_new,
    .tp_init = (initproc) Morris_init,
    .tp_dealloc = (destructor) Morris_dealloc,
    .tp_getset = get_Morris_sets,
    .tp_methods = Morris_methods,    
};


#endif // MORRIS_COUNTER_H