{"action":"created","comment":{"url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/comments/28937188","id":28937188,"diff_hunk":"@@ -0,0 +1,2364 @@\n+#include \"Python.h\"\n+\n+/*\n+NOTES:\n+- Python.h modifies assert() so it does not trigger program termination on failure. exit() terminates the interpreter.\n+- small memory allocations assumed to work.\n+- ndarrays not allowed to contain objects with structure.\n+\n+Common brief variable names and meanings\n+i, j : for-loop counters\n+len, n : the length of a list/tuple/array/string/etc object\n+lobj : list Python object\n+obj : generic Python object\n+robj : the Python object that will be returned by a function\n+sobj : string Python object\n+tobj : temporary Python object\n+*/\n+\n+#define TRUE 1\n+#define FALSE 0\n+#define SUCCESS 1\n+#define EXCEPTION_RAISED -1\n+#define BIG_ENDIAN \">\"\n+#define LITTLE_ENDIAN \"<\"\n+\n+#define LABRAD_BOOL_SIZE 1\n+#define LABRAD_INT_SIZE 4\n+#define LABRAD_LONG_SIZE 8\n+#define LABRAD_FLOAT_SIZE 8\n+\n+// supported internal data types\n+#define EMPTY 0\n+#define BOOL 1\n+#define UINT31 2\n+#define INT32 3\n+#define UINT32 4\n+#define FLOAT64 5\n+#define COMPLEX128 6\n+#define FLOAT64_SU 7\n+#define COMPLEX128_SU 8\n+#define STRING 9\n+#define CLUSTER 10\n+#define LIST 11\n+#define NDARRAY 12\n+#define NDARRAY_SU 13\n+#define TIME 14\n+#define ERROR 15\n+\n+// COBJ is an all-encompassing structure containing a wide enough variety of elements to represent any of the above types\n+typedef struct cobj COBJ;\n+\n+struct cobj {\n+    int type; // type of this cobj\n+    int subtype; // if NDARRAY(_SU), type of each element in array, if ERROR, specific type of ERROR\n+    int istype; // FALSE unless structure contains only type information\n+    int i; // good for BOOL, UINT31, INT32\n+    unsigned int n; // good for UINT32, length cobj_arr, length string, length dim_arr\n+    double x, y; // FLOAT64 and COMPLEX128 data\n+    char *str; // STRING (not owned), unit name (owned), and ERROR data (not owned)\n+    COBJ **cobj_arr; // sub cobjs contained in CLUSTER or LIST (owned)\n+    int *dim_arr; // dimensions of NDARRAY(_SU)\n+    void *arr; // data of NDARRAY(_SU)\n+    int isarrowned; // FALSE unless arr has be recreated to be a different type, usually arr is just a pointer to data in the pyobject.\n+    long long int secs, usecs; // TIME data\n+};\n+\n+// the following is a simplification of the real numpy PyArrayObject, use with caution\n+typedef struct PyArrayObject {\n+    PyObject_HEAD\n+    char *data;\n+    int nd;\n+    int *dimensions;\n+    int *strides;\n+    PyObject *base;\n+    void *descr;\n+    int flags;\n+    PyObject *weakreflist;\n+} PyArrayObject;\n+\n+// global variables, initialized by first function call (which will take much longer than subsequent calls)\n+int ft_gv_initialized = FALSE;\n+int ft_gv_system_big_endian = FALSE;\n+int ft_gv_send_big_endian = TRUE;\n+PyObject *ft_gv_0 = NULL;\n+PyObject *ft_gv_1 = NULL;\n+PyObject *ft_gv_2 = NULL;\n+PyObject *ft_gv_int_num_bits = NULL;\n+PyObject *ft_gv_int_num_bits_minus_1 = NULL;\n+PyObject *ft_gv_INT_MIN = NULL;\n+PyObject *ft_gv_INT_MAX = NULL;\n+PyObject *ft_gv_UINT_MAX = NULL;\n+PyObject *ft_gv_numpy = NULL;\n+PyTypeObject *ft_gv_numpy_ndarray = NULL;\n+PyTypeObject *ft_gv_numpy_bool_ = NULL;\n+PyTypeObject *ft_gv_numpy_int32 = NULL;\n+PyTypeObject *ft_gv_numpy_uint32 = NULL;\n+PyTypeObject *ft_gv_numpy_float64 = NULL;\n+PyTypeObject *ft_gv_numpy_complex128 = NULL;\n+PyObject *ft_gv_labrad_types = NULL;\n+PyObject *ft_gv_labrad_units = NULL;\n+PyTypeObject *ft_gv_labrad_units_WithUnit = NULL;\n+PyTypeObject *ft_gv_labrad_units_Value = NULL;\n+PyTypeObject *ft_gv_labrad_units_Complex = NULL;\n+PyTypeObject *ft_gv_labrad_units_ValueArray = NULL;\n+\n+// C equivalent of labrad.types.flatten, called in similar manner, although type tags must be strings\n+PyObject *ft_flatten(PyObject *self, PyObject *args, PyObject *keywds);\n+\n+// functions to create an internal C representation (COBJ) of supported Python objects\n+COBJ *ft_create_cobj_from_pyobj(PyObject *obj);\n+    COBJ *ft_allocate_memory_for_cobj();\n+    COBJ *ft_create_empty_cobj();\n+    COBJ *ft_create_cobj_from_pybool(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pyint_or_pylong(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pyfloat(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pycomplex(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pyWithUnit(PyObject *obj);\n+        char *ft_get_unit_string_ptr(PyObject *obj);\n+        COBJ *ft_convert_to_standard_units(COBJ *cobj);\n+    COBJ *ft_create_cobj_from_pystring(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pycluster(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pylist(PyObject *obj);\n+    COBJ *ft_create_cobj_from_pyndarray(PyObject *obj);\n+    \n+    COBJ *ft_create_cobj_from_bool(char b);\n+    COBJ *ft_create_cobj_from_int32(int i);\n+    COBJ *ft_create_cobj_from_uint32(unsigned int n);\n+    COBJ *ft_create_cobj_from_float64(double x);\n+    COBJ *ft_create_cobj_from_complex128(double x, double y);\n+\n+    COBJ *ft_create_cobj(int type, int istype);\n+    \n+    void ft_free_cobj(COBJ *cobj);\n+    \n+// functions to create a representation of the type of a COBJ\n+COBJ *ft_create_cobj_type(COBJ *cobj);\n+    COBJ *ft_create_list_type(COBJ *cobj);\n+\n+// necessarily complex function that takes an object/type and a type and makes the first more general to encompass the second. eg: int, uint returns float since this\n+// is the smallest type able to represent every int and uint value. When the second argument contains placeholder values (such as v[]) it can be modified. eg: v[s], v[]\n+// will not only return v[s] but modify the second argument to be v[s].\n+COBJ *ft_upgrade_first_cobj_to_encompass_second(COBJ *cobj1, COBJ *cobj2);\n+    COBJ *ft_upgrade_first_subtype_to_encompass_second(COBJ *cobj1, COBJ *cobj2);\n+    COBJ *ft_convert_ndarray_to_list(COBJ *cobj);\n+    COBJ *ft_create_cobj_from_ndarray_i(COBJ *cobj, int i);\n+    int ft_size(COBJ *cobj);\n+    int ft_size_subtype(COBJ *cobj);\n+    \n+// functions to convert a type tag into a cobj type\n+COBJ *ft_create_cobj_tag_from_pystring(PyObject *tt);\n+    COBJ *ft_create_cobj_tag_from_char_buf(char *buf, int *iptr, int n);\n+\n+// functions to initialize the global variables\n+int ft_initialize();\n+    int ft_system_supported();\n+    void ft_detect_system_endianness();\n+    int ft_set_send_endianness(PyObject *endianness);\n+\n+// function to print a cobj\n+void ft_print_cobj(COBJ *cobj);\n+    void ft_print_type(COBJ *cobj);\n+    void ft_print_subtype(COBJ *cobj);\n+    void ft_print_type_text(int type);\n+    \n+// after much preprocessing to build a clean C obj and C type, flatten\n+PyObject *ft_flatten_cobj(COBJ *cobj, COBJ *cobj_type);\n+    char *ft_create_type_string(COBJ *cobj, int *len_ptr);\n+    char *ft_append_string(char *str1, int *len1_ptr, char *str2, int len2);\n+    char *ft_append_char(char *str1, int *len1_ptr, char c);\n+    int ft_write_to_buf(COBJ *cobj, char *buf);\n+    void ft_appropriate_memcpy(void *dest, void *src, int len);\n+    void ft_reverse_memcpy(void *dest, void *src, int len);\n+\n+// C equivalent of labrad.types.unflatten, called in similar manner, although type tags must be strings\n+PyObject *ft_unflatten(PyObject *self, PyObject *args);\n+    PyObject *ft_unflatten_partial_parse(PyObject *o);\n+    PyObject *ft_unflatten_no_parse(char *s, int *si_ptr, COBJ *cobj_type);\n+\n+// currently immature test function\n+PyObject *ft_test(PyObject *self, PyObject *args);\n+\n+PyObject *ft_flatten(PyObject *self, PyObject *args, PyObject *keywds) {\n+    int i, n;\n+    PyObject *obj, *types, *endianness, *sobj, *lobj, *tobj, *robj;\n+    char *kwlist[] = {\"obj\", \"types\", \"endianness\", NULL};\n+    COBJ *cobj, *cobj_type, *cobj_tag;\n+    \n+    if (!ft_gv_initialized) {\n+        if (ft_initialize() == EXCEPTION_RAISED) goto exception;\n+    }\n+    \n+    obj = types = endianness = sobj = lobj = tobj = robj = NULL;\n+    cobj = cobj_type = cobj_tag = NULL;\n+    \n+    // Get the Python input objects, if provided. Reference counts not increased, pointers remain NULL if no object provided.\n+    if (!PyArg_ParseTupleAndKeywords(args, keywds, \"O|OO\", kwlist, &obj, &types, &endianness)) goto exception;\n+    \n+    if (endianness != NULL) {\n+        if (ft_set_send_endianness(endianness) == EXCEPTION_RAISED) goto exception;\n+    }\n+    \n+    // make types a non-empty list\n+    if (types == NULL) {\n+        types = sobj = PyString_FromString(\"?\"); // sobj will be DECREFed when lobj is\n+    }\n+    if (!PyList_Check(types)) {\n+        lobj = PyList_New(1); // 1.) need to check for lobj != NULL and DECREF when returning\n+        if (sobj == NULL) Py_INCREF(types);\n+        PyList_SET_ITEM(lobj, 0, types);\n+        types = lobj;\n+    }\n+    \n+    // convert Python obj into C obj\n+    cobj = ft_create_cobj_from_pyobj(obj); // 2.) need to free cobj when returning\n+    if (cobj == NULL) goto exception;\n+    \n+    // derive type from C obj\n+    cobj_type = ft_create_cobj_type(cobj); // 3.) need to free cobj_type when returning\n+    if (cobj_type == NULL) goto exception;\n+    \n+    // match C obj to provided type tag and flatten\n+    n = PyList_GET_SIZE(types);\n+    for (i=0; i<n; i++) {\n+        tobj = PyList_GET_ITEM(types, i); // borrowed reference\n+        cobj_tag = ft_create_cobj_tag_from_pystring(tobj); // 4.) need to free cobj_tag when returning\n+        if (cobj_tag == NULL) goto exception;\n+        cobj_type = ft_upgrade_first_cobj_to_encompass_second(cobj_type, cobj_tag);\n+        if (cobj_type != NULL) {\n+            cobj = ft_upgrade_first_cobj_to_encompass_second(cobj, cobj_type);\n+            if (cobj != NULL) {\n+                robj = ft_flatten_cobj(cobj, cobj_type);\n+                if (robj == NULL) goto exception;\n+                \n+                // clean up memory\n+                if (lobj != NULL) Py_DECREF(lobj);\n+                ft_free_cobj(cobj);\n+                ft_free_cobj(cobj_type);\n+                ft_free_cobj(cobj_tag);\n+                \n+                return robj;\n+            }\n+            cobj = ft_create_cobj_from_pyobj(obj);\n+        }\n+        ft_free_cobj(cobj_type);\n+        cobj_type = ft_create_cobj_type(cobj);\n+        ft_free_cobj(cobj_tag);\n+    }\n+    \n+exception:\n+    \n+    if (lobj != NULL) Py_DECREF(lobj);\n+    ft_free_cobj(cobj);\n+    ft_free_cobj(cobj_type);\n+    ft_free_cobj(cobj_tag);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_from_pyobj(PyObject *obj) {\n+    COBJ *cobj;\n+    \n+    cobj = NULL;\n+    \n+    if (obj == Py_None) {\n+        cobj = ft_create_empty_cobj();\n+    }\n+    else if (PyBool_Check(obj)) {\n+        cobj = ft_create_cobj_from_pybool(obj);\n+    }\n+    else if (PyInt_Check(obj) || PyLong_Check(obj)) {\n+        cobj = ft_create_cobj_from_pyint_or_pylong(obj);\n+        if (cobj == NULL) goto exception;\n+    }\n+    else if (PyFloat_Check(obj)) {\n+        cobj = ft_create_cobj_from_pyfloat(obj);\n+    }\n+    else if (PyComplex_Check(obj)) {\n+        cobj = ft_create_cobj_from_pycomplex(obj);\n+    }\n+    else if (PyObject_TypeCheck(obj, ft_gv_labrad_units_WithUnit)) {\n+        cobj = ft_create_cobj_from_pyWithUnit(obj);\n+        if (cobj == NULL) goto exception;\n+    }\n+    else if (PyString_Check(obj)) {\n+        cobj = ft_create_cobj_from_pystring(obj);\n+    }\n+    else if (PyTuple_Check(obj)) {\n+        cobj = ft_create_cobj_from_pycluster(obj);\n+        if (cobj == NULL) goto exception;\n+    }\n+    else if (PyList_Check(obj)) {\n+        cobj = ft_create_cobj_from_pylist(obj);\n+        if (cobj == NULL) goto exception;\n+    }\n+    else if (PyObject_TypeCheck(obj, ft_gv_numpy_ndarray)) {\n+        cobj = ft_create_cobj_from_pyndarray(obj);\n+        if (cobj == NULL) goto exception;\n+    }\n+    \n+    if (cobj == NULL) {\n+        PyErr_SetString(PyExc_TypeError, \"unsupported python object\");\n+        goto exception;\n+    }\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_allocate_memory_for_cobj() {\n+    return (COBJ *)malloc(sizeof(COBJ));\n+}\n+\n+COBJ *ft_create_empty_cobj() {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = EMPTY;\n+    cobj->istype = FALSE;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_pybool(PyObject *obj) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = BOOL;\n+    cobj->istype = FALSE;\n+    \n+    if (obj == Py_True) cobj->i = TRUE;\n+    else cobj->i = FALSE;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_pyint_or_pylong(PyObject *obj) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->istype = FALSE;\n+    \n+    if (PyObject_Compare(obj, ft_gv_0) < 0) {\n+        if (PyObject_Compare(obj, ft_gv_INT_MIN) < 0) {\n+            cobj->type = FLOAT64;\n+            cobj->x = PyLong_AsDouble(obj);\n+            if (PyErr_Occurred() != NULL) goto exception;\n+        }\n+        else {\n+            cobj->type = INT32;\n+            cobj->i = (int)PyInt_AsLong(obj);\n+        }\n+    }\n+    else if (PyObject_Compare(obj, ft_gv_UINT_MAX) > 0) {\n+        cobj->type = FLOAT64;\n+        cobj->x = PyLong_AsDouble(obj);\n+        if (PyErr_Occurred() != NULL) goto exception;\n+    }\n+    else if (PyObject_Compare(obj, ft_gv_INT_MAX) > 0) {\n+        cobj->type = UINT32;\n+        cobj->n = PyLong_AsUnsignedLong(obj);\n+    }\n+    else {\n+        cobj->type = UINT31;\n+        cobj->i = (int)PyInt_AsLong(obj);\n+    }\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    ft_free_cobj(cobj);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_from_pyfloat(PyObject *obj) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = FLOAT64;\n+    cobj->istype = FALSE;\n+    cobj->x = PyFloat_AS_DOUBLE(obj);\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_pycomplex(PyObject *obj) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = COMPLEX128;\n+    cobj->istype = FALSE;\n+    cobj->x = PyComplex_RealAsDouble(obj);\n+    cobj->y = PyComplex_ImagAsDouble(obj);\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_pyWithUnit(PyObject *obj) {\n+    PyObject *tobj;\n+    COBJ *cobj;\n+\n+    tobj = PyObject_GetAttrString(obj, \"_value\"); // need to DECREF tobj\n+    cobj = ft_create_cobj_from_pyobj(tobj);\n+    Py_DECREF(tobj);\n+    if (cobj == NULL) goto exception;\n+\n+    cobj->str = ft_get_unit_string_ptr(obj);\n+    cobj = ft_convert_to_standard_units(cobj);\n+    if (cobj == NULL) goto exception;\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    return NULL;\n+}\n+\n+char *ft_get_unit_string_ptr(PyObject *obj) {\n+    int n;\n+    char *str1, *str2;\n+    PyObject *tobj1, *tobj2;\n+    \n+    tobj1 = PyObject_GetAttrString(obj, \"unit\");\n+    tobj2 = PyObject_GetAttrString(tobj1, \"name\");\n+    str1 = ((PyStringObject *)tobj2)->ob_sval;\n+    n = PyString_GET_SIZE(tobj2);\n+    str2 = (char *)malloc((n+1)*sizeof(char));\n+    strcpy(str2, str1);\n+    Py_DECREF(tobj2);\n+    Py_DECREF(tobj1);\n+    \n+    return str2;\n+}\n+\n+COBJ *ft_convert_to_standard_units(COBJ *cobj) {\n+    if (cobj->type == FLOAT64) cobj->type = FLOAT64_SU;\n+    else if (cobj->type == COMPLEX128) cobj->type = COMPLEX128_SU;\n+    else if (cobj->type == NDARRAY) cobj->type = NDARRAY_SU;\n+    else {\n+        PyErr_SetString(PyExc_TypeError, \"unable to convert WithUnit to standard units (SU)\");\n+        ft_free_cobj(cobj);\n+        return NULL;\n+    }\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_pystring(PyObject *obj) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = STRING;\n+    cobj->istype = FALSE;\n+    cobj->str = ((PyStringObject *)obj)->ob_sval;\n+    cobj->n = PyString_GET_SIZE(obj);\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_pycluster(PyObject *obj) {\n+    unsigned int ui;\n+    COBJ *cobj;\n+    PyObject *tobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = CLUSTER;\n+    cobj->istype = FALSE;\n+    cobj->n = PyTuple_GET_SIZE(obj);\n+    cobj->cobj_arr = (COBJ **)calloc(cobj->n, sizeof(COBJ *));\n+    \n+    for (ui=0; ui<cobj->n; ui++) {\n+        tobj = PyTuple_GET_ITEM(obj, ui);\n+        cobj->cobj_arr[ui] = ft_create_cobj_from_pyobj(tobj);\n+        if (cobj->cobj_arr[ui] == NULL) goto exception;\n+    }\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    ft_free_cobj(cobj);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_from_pylist(PyObject *obj) {\n+    unsigned int ui;\n+    COBJ *cobj;\n+    PyObject *tobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = LIST;\n+    cobj->istype = FALSE;\n+    cobj->n = PyList_GET_SIZE(obj);\n+    cobj->cobj_arr = (COBJ **)calloc(cobj->n, sizeof(COBJ *));\n+    \n+    for (ui=0; ui<cobj->n; ui++) {\n+        tobj = PyList_GET_ITEM(obj, ui);\n+        cobj->cobj_arr[ui] = ft_create_cobj_from_pyobj(tobj);\n+        if (cobj->cobj_arr[ui] == NULL) goto exception;\n+    }\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    ft_free_cobj(cobj);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_from_pyndarray(PyObject *obj) {\n+    unsigned int ui;\n+    PyObject *tobj;\n+    PyTypeObject *type;\n+    PyArrayObject *aobj;\n+    COBJ *cobj;\n+    \n+    tobj = PyObject_GetAttrString(obj, \"dtype\");\n+    type = (PyTypeObject *)PyObject_GetAttrString(tobj, \"type\");\n+    Py_DECREF(tobj);\n+    \n+    aobj = (PyArrayObject *)obj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = NDARRAY;\n+    cobj->istype = FALSE;\n+    cobj->n = aobj->nd;\n+    cobj->dim_arr = (int *)malloc(cobj->n*sizeof(int));\n+    for (ui=0; ui<cobj->n; ui++) cobj->dim_arr[ui] = aobj->dimensions[ui];\n+    cobj->isarrowned = FALSE;\n+    cobj->arr = (void *)aobj->data;\n+    \n+    if (type == ft_gv_numpy_bool_) cobj->subtype = BOOL;\n+    else if (type == ft_gv_numpy_int32) cobj->subtype = INT32;\n+    else if (type == ft_gv_numpy_uint32) cobj->subtype = UINT32;\n+    else if (type == ft_gv_numpy_float64) cobj->subtype = FLOAT64;\n+    else if (type == ft_gv_numpy_complex128) cobj->subtype = COMPLEX128;\n+    else {\n+        PyErr_SetString(PyExc_TypeError, \"unsupported numpy ndarray dtype\");\n+        goto exception;\n+    }\n+    \n+    Py_DECREF(type);\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    ft_free_cobj(cobj);\n+    Py_DECREF(type);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_from_bool(char b) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = BOOL;\n+    cobj->istype = FALSE;\n+    cobj->i = (int)b;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_int32(int i) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = INT32;\n+    cobj->istype = FALSE;\n+    cobj->i = i;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_uint32(unsigned int n) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = UINT32;\n+    cobj->istype = FALSE;\n+    cobj->n = n;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_float64(double x) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = FLOAT64;\n+    cobj->istype = FALSE;\n+    cobj->x = x;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj_from_complex128(double x, double y) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = COMPLEX128;\n+    cobj->istype = FALSE;\n+    cobj->x = x;\n+    cobj->y = y;\n+    \n+    return cobj;\n+}\n+\n+COBJ *ft_create_cobj(int type, int istype) {\n+    COBJ *cobj;\n+    \n+    cobj = ft_allocate_memory_for_cobj();\n+    \n+    cobj->type = type;\n+    cobj->istype = istype;\n+    \n+    if (type == LIST) {\n+        cobj->n = 1;\n+        cobj->cobj_arr = (COBJ **)calloc(1, sizeof(COBJ *));\n+    }\n+    \n+    return cobj;\n+}\n+\n+void ft_free_cobj(COBJ *cobj) {\n+    unsigned int ui;\n+    \n+    if (cobj == NULL) return;\n+    \n+    switch (cobj->type) {\n+        case FLOAT64_SU:\n+        case COMPLEX128_SU:\n+        case NDARRAY_SU:\n+            free(cobj->str);\n+    }\n+    \n+    switch (cobj->type) {\n+        case CLUSTER:\n+        case LIST:\n+            for (ui=0; ui<cobj->n; ui++) {\n+                ft_free_cobj(cobj->cobj_arr[ui]);\n+            }\n+            free(cobj->cobj_arr);\n+    }\n+    \n+    switch (cobj->type) {\n+        case NDARRAY:\n+        case NDARRAY_SU:\n+            free(cobj->dim_arr);\n+            if (cobj->isarrowned) free(cobj->arr);\n+    }\n+    \n+    free(cobj);\n+}\n+\n+COBJ *ft_create_cobj_type(COBJ *cobj) {\n+    unsigned int ui;\n+    COBJ *cobj_type;\n+    \n+    cobj_type = calloc(1, sizeof(COBJ));\n+    \n+    if (cobj == NULL) {\n+        PyErr_SetString(PyExc_TypeError, \"NULL has no type\");\n+        goto exception;\n+    }\n+    \n+    cobj_type->type = cobj->type;\n+    cobj_type->istype = TRUE;\n+    \n+    if (cobj->type == FLOAT64_SU || cobj->type == COMPLEX128_SU || cobj->type == NDARRAY_SU) {\n+        cobj_type->str = (char *)malloc((strlen(cobj->str)+1)*sizeof(char));\n+        strcpy(cobj_type->str, cobj->str);\n+    }\n+    \n+    if (cobj->type == NDARRAY || cobj->type == NDARRAY_SU) {\n+        cobj_type->subtype = cobj->subtype;\n+        cobj_type->n = cobj->n;\n+        cobj_type->dim_arr = (int *)malloc(cobj->n*sizeof(int));\n+        for (ui=0; ui<cobj->n; ui++) cobj_type->dim_arr[ui] = cobj->dim_arr[ui];\n+        // following unnecessary due to use of calloc\n+        // cobj_type->isarrowned = FALSE;\n+        // cobj_type->arr = NULL;\n+        return cobj_type;\n+    }\n+    \n+    if (cobj->type == CLUSTER) {\n+        cobj_type->n = cobj->n;\n+        cobj_type->cobj_arr = (COBJ **)calloc(cobj->n, sizeof(COBJ *));\n+        for (ui=0; ui<cobj->n; ui++) {\n+            cobj_type->cobj_arr[ui] = ft_create_cobj_type(cobj->cobj_arr[ui]);\n+            if (cobj_type->cobj_arr[ui] == NULL) goto exception;\n+        }\n+        return cobj_type;\n+    }\n+    \n+    if (cobj->type == LIST) {\n+        cobj_type->n = 1;\n+        cobj_type->cobj_arr = (COBJ **)malloc(sizeof(COBJ *));\n+        cobj_type->cobj_arr[0] = ft_create_list_type(cobj);\n+        if (cobj_type->cobj_arr[0] == NULL) goto exception;\n+    }\n+\n+    return cobj_type;\n+    \n+exception:\n+    \n+    ft_free_cobj(cobj_type);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_list_type(COBJ *cobj) {\n+    unsigned int ui;\n+    COBJ *cobj_type1, *cobj_type2;\n+    \n+    if (cobj->n == 0) {\n+        cobj_type1 = calloc(1, sizeof(COBJ));\n+        cobj_type1->type = EMPTY;\n+        cobj_type1->istype = TRUE;\n+        return cobj_type1;\n+    }\n+    \n+    cobj_type1 = ft_create_cobj_type(cobj->cobj_arr[0]);\n+    if (cobj_type1 == NULL) goto exception;\n+    \n+    for (ui=1; ui<cobj->n; ui++) {\n+        cobj_type2 = ft_create_cobj_type(cobj->cobj_arr[ui]);\n+        if (cobj_type2 == NULL) {\n+            ft_free_cobj(cobj_type1);\n+            goto exception;\n+        }\n+        cobj_type1 = ft_upgrade_first_cobj_to_encompass_second(cobj_type1, cobj_type2);\n+        ft_free_cobj(cobj_type2);\n+        if (cobj_type1 == NULL) goto exception;\n+    }\n+    \n+    return cobj_type1;\n+    \n+exception:\n+    \n+    return NULL;\n+}\n+\n+// handles both cobj1->istype == TRUE and FALSE, cobj2->istype == TRUE always\n+COBJ *ft_upgrade_first_cobj_to_encompass_second(COBJ *cobj1, COBJ *cobj2) {\n+    unsigned int ui;\n+    COBJ *cobj_copy;\n+    /* int N1, N2; */\n+    \n+    // handle NULL, EMPTY\n+    if (cobj1 == NULL || cobj2 == NULL) goto exception;\n+    \n+    if (cobj1->type == EMPTY){\n+        if (cobj1->istype == FALSE) return cobj1;\n+        if (cobj2->type == EMPTY) return cobj1;\n+        else {\n+            ft_free_cobj(cobj1);\n+            return ft_create_cobj_type(cobj2);\n+        }\n+    }\n+        \n+    if (cobj2->type == EMPTY) return cobj1;\n+    \n+    // handle BOOL, STRING, TIME, ERROR\n+    if ((cobj1->type == BOOL && cobj2->type != BOOL) ||\n+        (cobj1->type == STRING && cobj2->type != STRING) ||\n+        (cobj1->type == TIME && cobj2->type != TIME) ||\n+        (cobj1->type == ERROR && cobj2->type != ERROR)) {\n+        goto exception;\n+    }\n+    \n+    if ((cobj1->type == BOOL && cobj2->type == BOOL) ||\n+        (cobj1->type == STRING && cobj2->type == STRING) ||\n+        (cobj1->type == TIME && cobj2->type == TIME) ||\n+        (cobj1->type == ERROR && cobj2->type == ERROR)) {\n+        return cobj1;\n+    }\n+    \n+    // handle CLUSTER\n+    if (cobj1->type == CLUSTER) {\n+        if (cobj2->type != CLUSTER || cobj1->n != cobj2->n) goto exception;\n+        \n+        for (ui=0; ui<cobj1->n; ui++) {\n+            cobj1->cobj_arr[ui] = ft_upgrade_first_cobj_to_encompass_second(cobj1->cobj_arr[ui], cobj2->cobj_arr[ui]);\n+            if (cobj1->cobj_arr[ui] == NULL) goto exception;\n+        }\n+        \n+        return cobj1;\n+    }\n+    \n+    // handle UINT31, INT32, UINT32, FLOAT64, COMPLEX128\n+    if (cobj1->type == UINT31) {\n+        if (cobj2->type == UINT31) {\n+            return cobj1;\n+        }\n+        if (cobj2->type == INT32) {\n+            cobj1->type = INT32;\n+            return cobj1;\n+        }\n+        if (cobj2->type == UINT32) {\n+            cobj1->type = UINT32;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->n = (unsigned int)cobj1->i;\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->type == FLOAT64) {\n+            cobj1->type = FLOAT64;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->x = (double)cobj1->i;\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128) {\n+            cobj1->type = COMPLEX128;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->x = (double)cobj1->i;\n+                cobj1->y = 0;\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == INT32) {\n+        if (cobj2->type == UINT31 || cobj2->type == INT32) {\n+            return cobj1;\n+        }\n+        if (cobj2->type == UINT32 || cobj2->type == FLOAT64) {\n+            cobj1->type = FLOAT64;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->x = (double)cobj1->i;\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128) {\n+            cobj1->type = COMPLEX128;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->x = (double)cobj1->i;\n+                cobj1->y = 0;\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == UINT32) {\n+        if (cobj2->type == UINT31 || cobj2->type == UINT32) {\n+            return cobj1;\n+        }\n+        if (cobj2->type == INT32 || cobj2->type == FLOAT64) {\n+            cobj1->type = FLOAT64;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->x = (double)cobj1->n;\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128) {\n+            cobj1->type = COMPLEX128;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->x = (double)cobj1->n;\n+                cobj1->y = 0;\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == FLOAT64) {\n+        if (cobj2->type == UINT31 || cobj2->type == INT32 || cobj2->type == UINT32 || cobj2->type == FLOAT64) {\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128) {\n+            cobj1->type = COMPLEX128;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->y = 0;\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->type == FLOAT64_SU && cobj2->str[0] == '*') {\n+            cobj2->type = FLOAT64;\n+            free(cobj2->str);\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128_SU && cobj2->str[0] == '*') {\n+            cobj2->type = COMPLEX128;\n+            free(cobj2->str);\n+            cobj1->type = COMPLEX128;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->y = 0;\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == COMPLEX128) {\n+        if (cobj2->type == UINT31 || cobj2->type == INT32 || cobj2->type == UINT32 || cobj2->type == FLOAT64 || cobj2->type == COMPLEX128) {\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128_SU && cobj2->str[0] == '*') {\n+            cobj2->type = COMPLEX128;\n+            free(cobj2->str);\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    // handle FLOAT64_SU, COMPLEX128_SU\n+    if (cobj1->type == FLOAT64_SU) {\n+        if (cobj2->type == FLOAT64_SU) {\n+            if (cobj2->str[0] == '*') {\n+                free(cobj2->str);\n+                cobj2->str = (char *)malloc((strlen(cobj1->str)+1)*sizeof(char));\n+                strcpy(cobj2->str, cobj1->str);\n+            }\n+            if (strcmp(cobj1->str, cobj2->str) != 0) goto exception;\n+            return cobj1;\n+        }\n+        if (cobj2->type == COMPLEX128_SU) {\n+            if (cobj2->str[0] == '*') {\n+                free(cobj2->str);\n+                cobj2->str = (char *)malloc((strlen(cobj1->str)+1)*sizeof(char));\n+                strcpy(cobj2->str, cobj1->str);\n+            }\n+            if (strcmp(cobj1->str, cobj2->str) != 0) goto exception;\n+            cobj1->type = COMPLEX128_SU;\n+            if (cobj1->istype == FALSE) {\n+                cobj1->y = 0;\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == COMPLEX128_SU) {\n+        if (cobj2->type == COMPLEX128_SU) {\n+            if (cobj2->str[0] == '*') {\n+                free(cobj2->str);\n+                cobj2->str = (char *)malloc((strlen(cobj1->str)+1)*sizeof(char));\n+                strcpy(cobj2->str, cobj1->str);\n+            }\n+            if (strcmp(cobj1->str, cobj2->str) != 0) goto exception;\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    // handle NDARRAY_SU\n+    if (cobj1->type == NDARRAY_SU) {\n+        if (cobj2->type == NDARRAY_SU) {\n+            if (cobj2->str[0] == '*') {\n+                free(cobj2->str);\n+                cobj2->str = (char *)malloc((strlen(cobj1->str)+1)*sizeof(char));\n+                strcpy(cobj2->str, cobj1->str);\n+            }\n+            if (strcmp(cobj1->str, cobj2->str) != 0) goto exception;\n+            if (cobj1->n != cobj2->n) goto exception;\n+            cobj1 = ft_upgrade_first_subtype_to_encompass_second(cobj1, cobj2);\n+            if (cobj1 == NULL) return NULL;\n+            for (ui=0; ui<cobj1->n; ui++) {\n+                if (cobj2->dim_arr[ui] == 0) cobj2->dim_arr[ui] = cobj1->dim_arr[ui];\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == NDARRAY) {\n+        if (cobj2->type == NDARRAY_SU && cobj2->str[0] == '*') {\n+            cobj2->type = NDARRAY;\n+            free(cobj2->str);\n+        }\n+        if (cobj2->type == NDARRAY) {\n+            if (cobj1->n != cobj2->n) goto exception;\n+            cobj1 = ft_upgrade_first_subtype_to_encompass_second(cobj1, cobj2);\n+            if (cobj1 == NULL) return NULL;\n+            for (ui=0; ui<cobj1->n; ui++) {\n+                if (cobj2->dim_arr[ui] == 0) cobj2->dim_arr[ui] = cobj1->dim_arr[ui];\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->type == LIST) {\n+            cobj1 = ft_convert_ndarray_to_list(cobj1);\n+            if (cobj1 == NULL) return NULL;\n+            return ft_upgrade_first_cobj_to_encompass_second(cobj1, cobj2);\n+        }\n+        goto exception;\n+    }\n+    \n+    if (cobj1->type == LIST) {\n+        if (cobj2->type == NDARRAY) {\n+            cobj_copy = ft_create_cobj_type(cobj2);\n+            cobj_copy = ft_convert_ndarray_to_list(cobj_copy);\n+            if (cobj_copy == NULL) goto exception;\n+            cobj1 = ft_upgrade_first_cobj_to_encompass_second(cobj1, cobj_copy);\n+            ft_free_cobj(cobj_copy);\n+            return cobj1;\n+        }\n+        if (cobj2->type == LIST) {\n+            if (cobj1->n == 0 || cobj2->cobj_arr[0]->type == EMPTY) return cobj1;\n+            for (ui=0; ui<cobj1->n; ui++) {\n+                cobj1->cobj_arr[ui] = ft_upgrade_first_cobj_to_encompass_second(cobj1->cobj_arr[ui], cobj2->cobj_arr[0]);\n+                if (cobj1->cobj_arr[ui] == NULL) goto exception;\n+            }\n+            return cobj1;\n+        }\n+        goto exception;\n+    }\n+    \n+    goto exception;\n+    \n+exception:\n+    \n+    PyErr_SetString(PyExc_TypeError, \"unable to upgrade one object to encompass another, inhomogeneous list or type tag/object mismatch\");\n+    ft_free_cobj(cobj1);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_upgrade_first_subtype_to_encompass_second(COBJ *cobj1, COBJ *cobj2) {\n+    unsigned int ui;\n+    int i, N;\n+    double *arr;\n+    \n+    // handle dimension number disparity\n+    if (cobj1->n != cobj2->n) {\n+        PyErr_SetString(PyExc_TypeError, \"ndarrays have different number of dimensions\");\n+        goto exception;\n+    }\n+    \n+    if (cobj1->subtype == cobj2->subtype) return cobj1;\n+    \n+    // handle BOOL disparity\n+    if (cobj1->subtype == BOOL || cobj2->subtype == BOOL) {\n+        PyErr_SetString(PyExc_TypeError, \"ndarrays have inconsistent bool/numeric types\");\n+        goto exception;\n+    }\n+    \n+    // upgrade data if required\n+    N = 1;\n+    for (ui=0; ui<cobj1->n; ui++) N *= cobj1->dim_arr[ui];\n+    \n+    if (cobj1->subtype == INT32 || cobj1->subtype == UINT32) {\n+        if (cobj2->subtype == INT32 || cobj2->subtype == UINT32 || cobj2->subtype == FLOAT64) {\n+            cobj1->subtype = FLOAT64;\n+            if (!cobj1->istype) {\n+                arr = (double *)malloc(N*LABRAD_FLOAT_SIZE);\n+                for (i=0; i<N; i++) arr[i] = ((double *)cobj1->arr)[i];\n+                cobj1->arr = arr;\n+                cobj1->isarrowned = TRUE;\n+            }\n+            return cobj1;\n+        }\n+        if (cobj2->subtype == COMPLEX128) {\n+            cobj1->subtype = COMPLEX128;\n+            if (!cobj1->istype) {\n+                arr = (double *)calloc(2*N, LABRAD_FLOAT_SIZE);\n+                for (i=0; i<2*N; i+=2) arr[i] = ((double *)cobj1->arr)[i];\n+                cobj1->arr = arr;\n+                cobj1->isarrowned = TRUE;\n+            }\n+            return cobj1;\n+        }\n+        PyErr_SetString(PyExc_TypeError, \"unsupported ndarray subtype\");\n+        goto exception;\n+    }\n+    \n+    if (cobj1->subtype == FLOAT64) {\n+        if (cobj2->subtype == INT32 || cobj2->subtype == UINT32 || cobj2->subtype == FLOAT64) return cobj1;\n+        if (cobj2->subtype == COMPLEX128) {\n+            cobj1->subtype = COMPLEX128;\n+            if (!cobj1->istype) {\n+                arr = (double *)calloc(2*N, LABRAD_FLOAT_SIZE);\n+                for (i=0; i<2*N; i+=2) arr[i] = ((double *)cobj1->arr)[i];\n+                cobj1->arr = arr;\n+                cobj1->isarrowned = TRUE;\n+            }\n+            return cobj1;\n+        }\n+        PyErr_SetString(PyExc_TypeError, \"unsupported ndarray subtype\");\n+        goto exception;\n+    }\n+    \n+    if (cobj1->subtype == COMPLEX128) return cobj1;\n+    \n+    PyErr_SetString(PyExc_TypeError, \"unsupported ndarray subtype\");\n+    goto exception;\n+    \n+exception:\n+    \n+    ft_free_cobj(cobj1);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_convert_ndarray_to_list(COBJ *cobj) {\n+    int i;\n+    COBJ *tcobj;\n+    \n+    if (cobj == NULL) {\n+        cobj = ft_create_empty_cobj();\n+        cobj->n = 0;\n+    }\n+    \n+    cobj->type = LIST;\n+    \n+    if (cobj->subtype != BOOL && cobj->subtype != INT32 && cobj->subtype != UINT32 && cobj->subtype != FLOAT64 && cobj->subtype != COMPLEX128) goto exception;\n+    \n+    if (cobj->n == 0) {\n+        if (!cobj->istype) {\n+            cobj->cobj_arr = NULL;\n+            return cobj;\n+        }\n+        cobj->n = 1;\n+        cobj->cobj_arr = (COBJ **)malloc(sizeof(COBJ *));\n+        cobj->cobj_arr[0] = calloc(1, sizeof(COBJ));\n+        cobj->cobj_arr[0]->type = EMPTY;\n+        cobj->cobj_arr[0]->istype = TRUE;\n+        return cobj;\n+    }\n+    \n+    \n+    if (!cobj->istype) {\n+        cobj->cobj_arr = (COBJ **)malloc(cobj->dim_arr[0]*sizeof(COBJ *));\n+    }\n+    else {\n+        cobj->cobj_arr = (COBJ **)malloc(sizeof(COBJ *));\n+    }\n+    \n+    if (cobj->n == 1) {\n+        if (!cobj->istype) {\n+            if (cobj->subtype == BOOL) {\n+                for (i=0; i<cobj->dim_arr[0]; i++) {\n+                    cobj->cobj_arr[i] = ft_create_cobj_from_bool(((char *)cobj->arr)[i]);\n+                }\n+            }\n+            else if (cobj->subtype == INT32) {\n+                for (i=0; i<cobj->dim_arr[0]; i++) {\n+                    cobj->cobj_arr[i] = ft_create_cobj_from_int32(((int *)cobj->arr)[i]);\n+                }\n+            }\n+            else if (cobj->subtype == UINT32) {\n+                for (i=0; i<cobj->dim_arr[0]; i++) {\n+                    cobj->cobj_arr[i] = ft_create_cobj_from_uint32(((unsigned int *)cobj->arr)[i]);\n+                }\n+            }\n+            else if (cobj->subtype == FLOAT64) {\n+                for (i=0; i<cobj->dim_arr[0]; i++) {\n+                    cobj->cobj_arr[i] = ft_create_cobj_from_float64(((double *)cobj->arr)[i]);\n+                }\n+            }\n+            else if (cobj->subtype == COMPLEX128) {\n+                for (i=0; i<cobj->dim_arr[0]; i++) {\n+                    cobj->cobj_arr[i] = ft_create_cobj_from_complex128(((double *)cobj->arr)[i<<1], ((double *)cobj->arr)[(i<<1)+1]);\n+                }\n+            }\n+            cobj->n = (unsigned int)cobj->dim_arr[0];\n+            return cobj;\n+        }\n+        else {\n+            cobj->cobj_arr[0] = calloc(1, sizeof(COBJ));\n+            cobj->cobj_arr[0]->type = cobj->subtype;\n+            cobj->cobj_arr[0]->istype = TRUE;\n+            return cobj;\n+        }\n+    }\n+    \n+    if (!cobj->istype) {\n+        for (i=0; i<cobj->dim_arr[0]; i++) {\n+            cobj->cobj_arr[i] = ft_create_cobj_from_ndarray_i(cobj, i);\n+            cobj->cobj_arr[i] = ft_convert_ndarray_to_list(cobj->cobj_arr[i]);\n+        }\n+        cobj->n = (unsigned int)cobj->dim_arr[0];\n+    }\n+    else {\n+        tcobj = cobj;\n+        while (cobj->n > 0) {\n+            cobj->n--;\n+            tcobj->cobj_arr = (COBJ **)malloc(sizeof(COBJ *));\n+            tcobj->cobj_arr[0] = calloc(1, sizeof(COBJ));\n+            tcobj = tcobj->cobj_arr[0];\n+            tcobj->type = LIST;\n+            tcobj->istype = TRUE;\n+            tcobj->n = 1;\n+        }\n+        cobj->n = 1;\n+        tcobj->type = cobj->subtype;\n+    }\n+    \n+    return cobj;\n+    \n+exception:\n+    \n+    PyErr_SetString(PyExc_TypeError, \"unsupported ndarray type, cannot convert to list\");\n+    ft_free_cobj(cobj);\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_from_ndarray_i(COBJ *cobj, int i) {\n+    COBJ *cobj_i;\n+    unsigned int ui;\n+    int N;\n+    \n+    cobj_i = ft_allocate_memory_for_cobj();\n+    \n+    cobj_i->type = NDARRAY;\n+    cobj_i->subtype = cobj->subtype;\n+    cobj_i->istype = FALSE;\n+    cobj_i->n = cobj->n - 1;\n+    cobj_i->dim_arr = (int *)malloc(cobj_i->n*sizeof(int));\n+    for (ui=0; ui<cobj_i->n; ui++) cobj_i->dim_arr[ui] = cobj->dim_arr[ui+1];\n+    \n+    N = 1;\n+    for (ui=0; ui<cobj_i->n; ui++) N *= cobj_i->dim_arr[ui];\n+    \n+    cobj_i->arr = (char *)cobj->arr + i*N*ft_size_subtype(cobj);\n+    \n+    return cobj_i;\n+}\n+\n+int ft_size(COBJ *cobj) {\n+    int type, size, N;\n+    unsigned int ui;\n+    \n+    if (cobj == NULL) return 0;\n+    \n+    type = cobj->type;\n+    \n+    if (type == EMPTY) return 0;\n+    if (type == BOOL) return LABRAD_BOOL_SIZE;\n+    if (type == UINT31 || type == INT32 || type == UINT32) return LABRAD_INT_SIZE;\n+    if (type == FLOAT64 || type == FLOAT64_SU) return LABRAD_FLOAT_SIZE;\n+    if (type == COMPLEX128 || type == COMPLEX128_SU) return 2*LABRAD_FLOAT_SIZE;\n+    if (type == STRING) return LABRAD_INT_SIZE + cobj->n;\n+    if (type == CLUSTER || type == LIST) {\n+        size = LABRAD_INT_SIZE;\n+        for (ui=0; ui<cobj->n; ui++) {\n+            size += ft_size(cobj->cobj_arr[ui]);\n+        }\n+        return size;\n+    }\n+    if (type == NDARRAY || type == NDARRAY_SU) {\n+        size = cobj->n*LABRAD_INT_SIZE;\n+        N = 1;\n+        for (ui=0; ui<cobj->n; ui++) N *= cobj->dim_arr[ui];\n+        size += N*ft_size_subtype(cobj);\n+        return size;\n+    }\n+    if (type == TIME) return 2*LABRAD_LONG_SIZE;\n+    if (type == ERROR) {\n+        size = 2*LABRAD_INT_SIZE;\n+        size += cobj->n;\n+        return size;\n+    }\n+    \n+    PyErr_SetString(PyExc_TypeError, \"unsupported object type\");\n+    \n+    return EXCEPTION_RAISED;\n+}\n+\n+int ft_size_subtype(COBJ *cobj) {\n+    int subtype;\n+    \n+    subtype = cobj->subtype;\n+    \n+    if (subtype == BOOL) return LABRAD_BOOL_SIZE;\n+    if (subtype == UINT31 || subtype == INT32 || subtype == UINT32) return LABRAD_INT_SIZE;\n+    if (subtype == FLOAT64) return LABRAD_FLOAT_SIZE;\n+    if (subtype == COMPLEX128) return 2*LABRAD_FLOAT_SIZE;\n+    \n+    PyErr_SetString(PyExc_TypeError, \"unsupported ndarray subtype\");\n+    \n+    return EXCEPTION_RAISED;\n+}\n+\n+COBJ *ft_create_cobj_tag_from_pystring(PyObject *tt) {\n+    int i, n, len;\n+    char *buf;\n+    COBJ *cobj_tag, *cobj_cluster;\n+    \n+    if (!PyString_Check(tt)) {\n+        PyErr_SetString(PyExc_TypeError, \"type tag must be a string\");\n+        goto exception;\n+    }\n+    \n+    i = 0;\n+    n = PyString_GET_SIZE(tt);\n+    buf = ((PyStringObject *)tt)->ob_sval;\n+    // printf(\"buf: %s\\n\", buf);\n+    \n+    if (n == 0) {\n+        PyErr_SetString(PyExc_SyntaxError, \"type tag must have non-zero length\");\n+        goto exception;\n+    }\n+    \n+    cobj_tag = ft_create_cobj_tag_from_char_buf(buf, &i, n);\n+    if (cobj_tag == NULL) goto exception;\n+    // printf(\"after ft_create_type_tree_char i: %d, n: %d\\n\", i, n);\n+    \n+    if (i != n) {\n+        len = 0;\n+        cobj_cluster = ft_create_cobj(CLUSTER, TRUE);\n+        cobj_cluster->cobj_arr = (COBJ **)malloc(sizeof(COBJ *));\n+        cobj_cluster->cobj_arr[len] = cobj_tag;\n+        cobj_tag = cobj_cluster;\n+        len = 1;\n+        while (i < n) {\n+            cobj_tag->cobj_arr = (COBJ **)realloc(cobj_tag->cobj_arr, (len+1)*sizeof(COBJ *));\n+            cobj_tag->cobj_arr[len] = ft_create_cobj_tag_from_char_buf(buf, &i, n);\n+            if (cobj_tag->cobj_arr[len] == NULL) {\n+                ft_free_cobj(cobj_tag);\n+                goto exception;\n+            }\n+            len++;\n+        }\n+        cobj_tag->n = len;\n+        return cobj_tag;\n+        \n+        // PyErr_SetString(PyExc_SyntaxError, \"superfluous characters at end of type tag\");\n+        // ft_free_cobj(cobj_tag);\n+        // goto exception;\n+    }\n+    \n+    return cobj_tag;\n+    \n+exception:\n+    \n+    return NULL;\n+}\n+\n+COBJ *ft_create_cobj_tag_from_char_buf(char *buf, int *iptr, int n) {\n+    int i, j, len;\n+    COBJ *cobj_tag, *sub;\n+    \n+    i = *iptr;\n+    \n+    if (i == n) return NULL;\n+    \n+    if (buf[i] == '_' || buf[i] == '?') {\n+        (*iptr)++;\n+        return ft_create_cobj(EMPTY, TRUE);\n+    }\n+    else if (buf[i] == 'b') {\n+        (*iptr)++;\n+        return ft_create_cobj(BOOL, TRUE);\n+    }\n+    else if (buf[i] == 'i') {\n+        (*iptr)++;\n+        return ft_create_cobj(INT32, TRUE);\n+    }\n+    else if (buf[i] == 'w') {\n+        (*iptr)++;\n+        return ft_create_cobj(UINT32, TRUE);\n+    }\n+    else if (buf[i] == 'v' || buf[i] == 'c') {\n+        if (buf[i] == 'v') cobj_tag = ft_create_cobj(FLOAT64_SU, TRUE);\n+        else cobj_tag = ft_create_cobj(COMPLEX128_SU, TRUE);\n+        if (i == n-1 || buf[i+1] != '[') {\n+            cobj_tag->str = (char *)malloc(2*sizeof(char));\n+            strcpy(cobj_tag->str, \"*\");\n+            (*iptr)++;\n+            return cobj_tag;\n+        }\n+        i += 2;\n+        len = 0;\n+        while (i+len < n) {\n+            if (buf[i + len] == ']') break;\n+            len++;\n+        }\n+        if (i+len >= n) {\n+            PyErr_SetString(PyExc_SyntaxError, \"missing closing ]\");\n+            ft_free_cobj(cobj_tag);\n+            goto exception;\n+        }\n+        if (len == 0) {\n+            cobj_tag->str = (char *)malloc(2*sizeof(char));\n+            strcpy(cobj_tag->str, \"*\");\n+            *iptr = i + 1;\n+            return cobj_tag;\n+        }\n+        cobj_tag->str = (char *)malloc((len+1)*sizeof(char));\n+        strncpy(cobj_tag->str, buf + i, len);\n+        cobj_tag->str[len] = '\\0';\n+        *iptr = i + len + 1;\n+        return cobj_tag;\n+    }\n+    else if (buf[i] == 's') {\n+        cobj_tag = ft_create_cobj(STRING, TRUE);\n+        (*iptr)++;\n+        return cobj_tag;\n+    }\n+    else if (buf[i] == '*') {\n+        i++;\n+        if (i == n) {\n+            PyErr_SetString(PyExc_SyntaxError, \"list with no sub type tag\");\n+            goto exception;\n+        }\n+        // take care of cases that imply '*' must correspond to a LIST\n+        switch (buf[i]) {\n+            case 's':\n+            case '(':\n+            case '*':\n+            case 't':\n+            case 'E':\n+            case '_':\n+            case '?':\n+                cobj_tag = ft_create_cobj(LIST, TRUE);\n+                cobj_tag->cobj_arr[0] = ft_create_cobj_tag_from_char_buf(buf, &i, n);\n+                if (cobj_tag->cobj_arr[0] == NULL) {\n+                    ft_free_cobj(cobj_tag);\n+                    goto exception;\n+                }\n+                *iptr = i;\n+                return cobj_tag;\n+        }\n+        len = 0;\n+        while (i+len < n && isdigit(buf[i+len])) len++;\n+        if (i+len >= n) {\n+            PyErr_SetString(PyExc_SyntaxError, \"ndarray with no sub type tag\");\n+            goto exception;\n+        }\n+        if (len > 1) {\n+            PyErr_SetString(PyExc_SyntaxError, \"ndarrays of dimension > 9 not supported\");\n+            goto exception;\n+        }\n+        if (buf[i] == '0') {\n+            PyErr_SetString(PyExc_SyntaxError, \"ndarrays of dimension 0 not supported\");\n+            goto exception;\n+        }\n+        switch (buf[i+len]) {\n+            case 's':\n+            case '(':\n+            case '*':\n+            case 't':\n+            case 'E':\n+            case '_':\n+            case '?':\n+                cobj_tag = sub = ft_create_cobj(LIST, TRUE);\n+                for (j=1; j<buf[i]-'0'; j++) {\n+                    sub->cobj_arr[0] = ft_create_cobj(LIST, TRUE);\n+                    sub = sub->cobj_arr[0];\n+                }\n+                i++;\n+                sub->cobj_arr[0] = ft_create_cobj_tag_from_char_buf(buf, &i, n);\n+                if (cobj_tag->cobj_arr[0] == NULL) {\n+                    ft_free_cobj(cobj_tag);\n+                    goto exception;\n+                }\n+                *iptr = i;\n+                return cobj_tag;\n+                \n+                // PyErr_SetString(PyExc_SyntaxError, \"unsupported ndarray subtype\");\n+                // goto exception;\n+        }\n+        cobj_tag = ft_create_cobj(NDARRAY_SU, TRUE);\n+        if (len == 0) cobj_tag->n = 1;\n+        else cobj_tag->n = buf[i] - '0';\n+        cobj_tag->dim_arr = (int *)calloc(cobj_tag->n, sizeof(int));\n+        i = i + len;\n+        sub = ft_create_cobj_tag_from_char_buf(buf, &i, n);\n+        switch (sub->type) {\n+            case BOOL:\n+            case INT32:\n+            case UINT32:\n+                cobj_tag->type = NDARRAY;\n+            case FLOAT64_SU:\n+            case COMPLEX128_SU:\n+                cobj_tag->subtype = sub->type;\n+                break;\n+            default:\n+                PyErr_SetString(PyExc_SyntaxError, \"unsupported ndarray subtype\");\n+                ft_free_cobj(cobj_tag);\n+                ft_free_cobj(sub);\n+                goto exception;\n+        }\n+        switch (sub->type) {\n+            case FLOAT64_SU:\n+            case COMPLEX128_SU:\n+                cobj_tag->str = (char *)malloc((strlen(sub->str)+1)*sizeof(char));\n+                strcpy(cobj_tag->str, sub->str);\n+        }\n+        ft_free_cobj(sub);\n+        *iptr = i;\n+        return cobj_tag;\n+    }\n+    else if (buf[i] == '(') {\n+        i++;\n+        if (i == n) {\n+            PyErr_SetString(PyExc_SyntaxError, \"tuple with no )\");\n+            goto exception;\n+        }\n+        cobj_tag = ft_create_cobj(CLUSTER, TRUE);\n+        cobj_tag->cobj_arr = NULL;\n+        len = 0;\n+        \n+        while (i < n && buf[i] != ')') {\n+            cobj_tag->cobj_arr = (COBJ **)realloc(cobj_tag->cobj_arr, (len+1)*sizeof(COBJ *));\n+            cobj_tag->cobj_arr[len] = ft_create_cobj_tag_from_char_buf(buf, &i, n);\n+            if (cobj_tag->cobj_arr[len] == NULL) {\n+                ft_free_cobj(cobj_tag);\n+                goto exception;\n+            }\n+            len++;\n+        }\n+        if (i == n) {\n+            PyErr_SetString(PyExc_SyntaxError, \"tuple with no )\");\n+            goto exception;\n+        }\n+        *iptr = i+1;\n+        cobj_tag->n = len;\n+        return cobj_tag;\n+    }\n+    else {\n+        PyErr_SetString(PyExc_SyntaxError, \"invalid type tag\");\n+        goto exception;\n+    }\n+    \n+exception:\n+    \n+    return NULL;\n+}\n+\n+int ft_initialize() {\n+    PyObject *tobj;\n+    \n+    if (!ft_system_supported()) {\n+        PyErr_SetString(PyExc_AssertionError, \"system has unsupported data type sizes\");\n+        goto exception;\n+    }\n+\n+    ft_detect_system_endianness();\n+    \n+    ft_gv_0 = PyInt_FromLong(0);\n+    ft_gv_1 = PyInt_FromLong(1);\n+    ft_gv_2 = PyInt_FromLong(2);\n+    ft_gv_int_num_bits = PyInt_FromLong(8*LABRAD_INT_SIZE);\n+    ft_gv_int_num_bits_minus_1 = PyInt_FromLong(8*LABRAD_INT_SIZE-1);\n+    \n+    tobj = PyNumber_Power(ft_gv_2, ft_gv_int_num_bits, Py_None);\n+    ft_gv_UINT_MAX = PyNumber_Subtract(tobj, ft_gv_1);\n+    Py_DECREF(tobj);\n+    \n+    tobj = PyNumber_Power(ft_gv_2, ft_gv_int_num_bits_minus_1, Py_None);\n+    ft_gv_INT_MIN = PyNumber_Negative(tobj);\n+    Py_DECREF(tobj);\n+    \n+    tobj = PyNumber_Power(ft_gv_2, ft_gv_int_num_bits_minus_1, Py_None);\n+    ft_gv_INT_MAX = PyNumber_Subtract(tobj, ft_gv_1);\n+    Py_DECREF(tobj);\n+    \n+    ft_gv_numpy = PyImport_ImportModule(\"numpy\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+    \n+    ft_gv_numpy_ndarray = (PyTypeObject *)PyObject_GetAttrString(ft_gv_numpy, \"ndarray\");\n+    ft_gv_numpy_bool_ = (PyTypeObject *)PyObject_GetAttrString(ft_gv_numpy, \"bool_\");\n+    ft_gv_numpy_int32 = (PyTypeObject *)PyObject_GetAttrString(ft_gv_numpy, \"int32\");\n+    ft_gv_numpy_uint32 = (PyTypeObject *)PyObject_GetAttrString(ft_gv_numpy, \"uint32\");\n+    ft_gv_numpy_float64 = (PyTypeObject *)PyObject_GetAttrString(ft_gv_numpy, \"float64\");\n+    ft_gv_numpy_complex128 = (PyTypeObject *)PyObject_GetAttrString(ft_gv_numpy, \"complex128\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+    \n+    ft_gv_labrad_types = PyImport_ImportModule(\"labrad.types\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+\n+    ft_gv_labrad_units = PyImport_ImportModule(\"labrad.units\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+\n+    ft_gv_labrad_units_WithUnit = (PyTypeObject *)PyObject_GetAttrString(ft_gv_labrad_units, \"WithUnit\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+    \n+    ft_gv_labrad_units_Value = (PyTypeObject *)PyObject_GetAttrString(ft_gv_labrad_units, \"Value\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+    \n+    ft_gv_labrad_units_Complex = (PyTypeObject *)PyObject_GetAttrString(ft_gv_labrad_units, \"Complex\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+    \n+    ft_gv_labrad_units_ValueArray = (PyTypeObject *)PyObject_GetAttrString(ft_gv_labrad_units, \"ValueArray\");\n+    if (PyErr_Occurred() != NULL) goto exception;\n+    \n+    ft_gv_initialized = TRUE;\n+\n+    return SUCCESS;\n+    \n+exception:\n+\n+    return EXCEPTION_RAISED;\n+}\n+ \n+int ft_system_supported() {\n+    if (sizeof(char) != LABRAD_BOOL_SIZE ||\n+        sizeof(int) != LABRAD_INT_SIZE ||\n+        sizeof(long long int) != LABRAD_LONG_SIZE ||\n+        sizeof(double) != LABRAD_FLOAT_SIZE)\n+        return FALSE;\n+    \n+    return TRUE;\n+}\n+\n+void ft_detect_system_endianness() {\n+    int i;\n+    char buf[LABRAD_INT_SIZE];\n+    \n+    i = 1;\n+    memcpy(buf, &i, LABRAD_INT_SIZE);\n+    \n+    if (buf[0] == 0) ft_gv_system_big_endian = TRUE;\n+    else ft_gv_system_big_endian = FALSE;\n+}\n+\n+int ft_set_send_endianness(PyObject *endianness) {\n+    PyObject *big_endian, *little_endian;\n+    \n+    big_endian = PyString_FromString(BIG_ENDIAN);\n+    little_endian = PyString_FromString(LITTLE_ENDIAN);\n+    \n+    if (!PyString_Check(endianness)) {\n+        PyErr_SetString(PyExc_TypeError, \"endianness must be '>' or '<'\");\n+        goto exception;\n+    }\n+    \n+    if (PyObject_RichCompareBool(endianness, big_endian, Py_EQ) == TRUE) {\n+        ft_gv_send_big_endian = TRUE;\n+    }\n+    else if (PyObject_RichCompareBool(endianness, little_endian, Py_EQ) == TRUE) {\n+        ft_gv_send_big_endian = FALSE;\n+    }\n+    else {\n+        PyErr_SetString(PyExc_SyntaxError, \"endianness must be '>' or '<'\");\n+        goto exception;\n+    }\n+    \n+    Py_DECREF(big_endian);\n+    Py_DECREF(little_endian);\n+    \n+    return TRUE;\n+    \n+exception:\n+    \n+    Py_DECREF(big_endian);\n+    Py_DECREF(little_endian);\n+    \n+    return FALSE;\n+}\n+\n+void ft_print_cobj(COBJ *cobj) {\n+    unsigned int ui, N;\n+    \n+    if (cobj == NULL) return;\n+    \n+    printf(\"%p\\n\", cobj);\n+    ft_print_type(cobj);\n+    \n+    if (cobj->istype) printf(\"istype: TRUE\\n\");\n+    else printf(\"istype: FALSE\\n\");\n+    \n+    switch (cobj->type) {\n+        case NDARRAY:\n+        case NDARRAY_SU:\n+        case ERROR:\n+            ft_print_subtype(cobj);\n+    }\n+    if (!cobj->istype) {\n+        switch (cobj->type) {\n+            case BOOL:\n+                if (cobj->i == FALSE) printf(\"i: FALSE\\n\");\n+                else printf(\"i: TRUE\\n\");\n+                break;\n+            case UINT31:\n+            case INT32:\n+                printf(\"i: %d\\n\", cobj->i);\n+                break;\n+        }\n+    }\n+    switch (cobj->type) {\n+        case CLUSTER:\n+        case NDARRAY:\n+        case NDARRAY_SU:\n+            printf(\"n: %u\\n\", cobj->n);\n+            break;\n+        case UINT32:\n+        case LIST:\n+        case STRING:\n+            if (!cobj->istype) {\n+                printf(\"n: %u\\n\", cobj->n);\n+            }\n+    }\n+    if (!cobj->istype) {\n+        switch (cobj->type) {\n+            case FLOAT64:\n+            case FLOAT64_SU:\n+            case COMPLEX128:\n+            case COMPLEX128_SU:\n+                printf(\"x: %g\\n\", cobj->x);\n+        }\n+        switch (cobj->type) {\n+            case COMPLEX128:\n+            case COMPLEX128_SU:\n+                printf(\"y: %g\\n\", cobj->y);\n+        }\n+    }\n+    switch (cobj->type) {\n+        case FLOAT64_SU:\n+        case COMPLEX128_SU:\n+        case NDARRAY_SU:\n+            printf(\"str: %s\\n\", cobj->str);\n+            break;\n+        case STRING:\n+        case ERROR:\n+            if (!cobj->istype) {\n+                printf(\"str: %s\\n\", cobj->str);\n+            }\n+    }\n+    switch (cobj->type) {\n+        case CLUSTER:\n+        case LIST:\n+            for (ui=0; ui<cobj->n; ui++) {\n+                printf(\"cobj_arr[%u]: %p\\n\", ui, cobj->cobj_arr[ui]);\n+            }\n+            for (ui=0; ui<cobj->n; ui++) {\n+                printf(\"\\n\");\n+                ft_print_cobj(cobj->cobj_arr[ui]);\n+            }\n+    }\n+    switch (cobj->type) {\n+        case NDARRAY:\n+        case NDARRAY_SU:\n+            N = 1;\n+            for (ui=0; ui<cobj->n; ui++) {\n+                printf(\"dim_arr[%u]: %d\\n\", ui, cobj->dim_arr[ui]);\n+                N *= cobj->dim_arr[ui];\n+            }\n+            if (!cobj->istype) {\n+                for (ui=0; ui<N; ui++) {\n+                    if (cobj->subtype == BOOL) {\n+                        printf(\"arr[%u]: %d\\n\", ui, ((char *)cobj->arr)[ui]);\n+                    }\n+                    else if (cobj->subtype == UINT31 || cobj->subtype == INT32) {\n+                        printf(\"arr[%u]: %d\\n\", ui, ((int *)cobj->arr)[ui]);\n+                    }\n+                    else if (cobj->subtype == UINT32) {\n+                        printf(\"arr[%u]: %u\\n\", ui, ((unsigned int *)cobj->arr)[ui]);\n+                    }\n+                    else if (cobj->subtype == FLOAT64) {\n+                        printf(\"arr[%u]: %g\\n\", ui, ((double *)cobj->arr)[ui]);\n+                    }\n+                    else if (cobj->subtype == COMPLEX128) {\n+                        printf(\"arr[%u]: %g + %gj\\n\", ui, ((double *)cobj->arr)[2*ui], ((double *)cobj->arr)[2*ui+1]);\n+                    }\n+                }\n+            }\n+    }\n+    if (!cobj->istype) {\n+        switch (cobj->type) {\n+            case TIME:\n+                printf(\"secs: %ld\\n\", cobj->secs);\n+                printf(\"usecs: %ld\\n\", cobj->usecs);\n+        }\n+    }\n+    \n+    // printf(\"\\n\");\n+}\n+\n+void ft_print_type(COBJ *cobj) {\n+    printf(\"type: \");\n+    ft_print_type_text(cobj->type);\n+    printf(\"\\n\");\n+}\n+\n+void ft_print_subtype(COBJ *cobj) {\n+    printf(\"subtype: \");\n+    ft_print_type_text(cobj->subtype);\n+    printf(\"\\n\");\n+}\n+\n+void ft_print_type_text(int type) {\n+   switch (type) {\n+        case EMPTY:\n+            printf(\"EMPTY\");\n+            break;\n+        case BOOL:\n+            printf(\"BOOL\");\n+            break;\n+        case UINT31:\n+            printf(\"UINT31\");\n+            break;\n+        case INT32:\n+            printf(\"INT32\");\n+            break;\n+        case UINT32:\n+            printf(\"UINT32\");\n+            break;\n+        case FLOAT64:\n+            printf(\"FLOAT64\");\n+            break;\n+        case COMPLEX128:\n+            printf(\"COMPLEX128\");\n+            break;\n+        case FLOAT64_SU:\n+            printf(\"FLOAT64_SU\");\n+            break;\n+        case COMPLEX128_SU:\n+            printf(\"COMPLEX128_SU\");\n+            break;\n+        case STRING:\n+            printf(\"STRING\");\n+            break;\n+        case CLUSTER:\n+            printf(\"CLUSTER\");\n+            break;\n+        case LIST:\n+            printf(\"LIST\");\n+            break;\n+        case NDARRAY:\n+            printf(\"NDARRAY\");\n+            break;\n+        case NDARRAY_SU:\n+            printf(\"NDARRAY_SU\");\n+            break;\n+        case TIME:\n+            printf(\"TIME\");\n+            break;\n+        case ERROR:\n+            printf(\"ERROR\");\n+            break;\n+    }\n+}\n+\n+PyObject *ft_flatten_cobj(COBJ *cobj, COBJ *cobj_type) {\n+    int size, len;\n+    char *buf, *str;\n+    PyObject *tup;\n+    PyStringObject *so;\n+    \n+    str = NULL;\n+    tup = NULL;\n+    so = NULL;\n+     \n+    size = ft_size(cobj);\n+    str = ft_create_type_string(cobj_type, &len);\n+    \n+    tup = PyTuple_New(2);\n+    \n+    so = (PyStringObject *)PyString_FromStringAndSize(NULL, size);\n+    if (so == NULL) {\n+        PyErr_SetString(PyExc_MemoryError, \"out of memory\");\n+        goto exception;\n+    }\n+    \n+    buf = so->ob_sval;\n+    ft_write_to_buf(cobj, buf);\n+    PyTuple_SET_ITEM(tup, 0, (PyObject *)so);\n+    \n+    so = (PyStringObject *)PyString_FromStringAndSize(NULL, len);\n+    if (so == NULL) {\n+        PyErr_SetString(PyExc_MemoryError, \"out of memory\");\n+        goto exception;\n+    }\n+\n+    buf = so->ob_sval;\n+    memcpy(buf, str, len);\n+    free(str);\n+    PyTuple_SET_ITEM(tup, 1, (PyObject *)so);\n+    \n+    return tup;\n+    \n+exception:\n+    \n+    if (str != NULL) free(str);\n+    \n+    if (tup != NULL) Py_DECREF(tup);\n+    else if (so != NULL) Py_DECREF(so);\n+    \n+    return NULL;\n+}\n+\n+char *ft_create_type_string(COBJ *cobj, int *len_ptr) {\n+    unsigned int ui;\n+    int len1, len2;\n+    char *str1, *str2;\n+    COBJ *cobj2;\n+    \n+    len1 = len2 = 0;\n+    str1 = str2 = NULL;\n+    \n+    if (cobj->type == NDARRAY || cobj->type == NDARRAY_SU) {\n+        str1 = ft_append_char(str1, &len1, '*');\n+        str1 = ft_append_char(str1, &len1, '0' + cobj->n);\n+        if (cobj->subtype == BOOL) {\n+            str1 = ft_append_char(str1, &len1, 'b');\n+        }\n+        else if (cobj->subtype == INT32) {\n+            str1 = ft_append_char(str1, &len1, 'i');\n+        }\n+        else if (cobj->subtype == UINT32) {\n+            str1 = ft_append_char(str1, &len1, 'w');\n+        }\n+        else if (cobj->subtype == FLOAT64) {\n+            str1 = ft_append_char(str1, &len1, 'v');\n+        }\n+        else if (cobj->subtype == COMPLEX128) {\n+            str1 = ft_append_char(str1, &len1, 'c');\n+        }\n+    }\n+    else if (cobj->type == LIST) {\n+        str1 = ft_append_char(str1, &len1, '*');\n+        if (cobj->n == 0) {\n+            str1 = ft_append_char(str1, &len1, '_');\n+        }\n+        else {\n+            cobj2 = cobj->cobj_arr[0];\n+            str2 = ft_create_type_string(cobj2, &len2);\n+            str1 = ft_append_string(str1, &len1, str2, len2);\n+            free(str2);\n+        }\n+    }\n+    else if (cobj->type == CLUSTER) {\n+        str1 = ft_append_char(str1, &len1, '(');\n+        for (ui=0; ui<cobj->n; ui++) {\n+            cobj2 = cobj->cobj_arr[ui];\n+            str2 = ft_create_type_string(cobj2, &len2);\n+            str1 = ft_append_string(str1, &len1, str2, len2);\n+            free(str2);\n+            len2 = 0;\n+            str2 = NULL;\n+        }\n+        str1 = ft_append_char(str1, &len1, ')');\n+    }\n+    else if (cobj->type == BOOL) {\n+        str1 = ft_append_char(str1, &len1, 'b');\n+    }\n+    else if (cobj->type == UINT31 || cobj->type == INT32) {\n+        str1 = ft_append_char(str1, &len1, 'i');\n+    }\n+    else if (cobj->type == UINT32) {\n+        str1 = ft_append_char(str1, &len1, 'w');\n+    }\n+    else if (cobj->type == FLOAT64) {\n+        str1 = ft_append_string(str1, &len1, \"v[]\", 3);\n+    }\n+    else if (cobj->type == COMPLEX128) {\n+        str1 = ft_append_string(str1, &len1, \"c[]\", 3);\n+    }\n+    else if (cobj->type == STRING) {\n+        str1 = ft_append_char(str1, &len1, 's');\n+    }\n+    else if (cobj->type == EMPTY) {\n+        str1 = ft_append_char(str1, &len1, '_');\n+    }\n+    \n+    if (cobj->type == NDARRAY_SU || cobj->type == FLOAT64_SU || cobj->type == COMPLEX128_SU) {\n+        if (cobj->type == FLOAT64_SU) str1 = ft_append_char(str1, &len1, 'v');\n+        else if (cobj->type == COMPLEX128_SU) str1 = ft_append_char(str1, &len1, 'c');\n+        str1 = ft_append_char(str1, &len1, '[');\n+        str1 = ft_append_string(str1, &len1, cobj->str, strlen(cobj->str));\n+        str1 = ft_append_char(str1, &len1, ']');\n+    }\n+    \n+    *len_ptr = len1;\n+    return str1;\n+}\n+\n+char *ft_append_string(char *str1, int *len1_ptr, char *str2, int len2) {\n+    int total = *len1_ptr + len2;\n+    \n+    str1 = (char *)realloc(str1, total);\n+    memcpy(str1 + *len1_ptr, str2, len2);\n+    *len1_ptr = total;\n+    \n+    return str1;\n+}\n+\n+char *ft_append_char(char *str1, int *len1_ptr, char c) {\n+    int total = *len1_ptr + 1;\n+    \n+    str1 = (char *)realloc(str1, total);\n+    str1[*len1_ptr] = c;\n+    *len1_ptr = total;\n+    \n+    return str1;\n+}\n+\n+int ft_write_to_buf(COBJ *cobj, char *buf) {\n+    unsigned int ui;\n+    int i, n, size = 0;\n+    \n+    if (cobj->type == EMPTY) {\n+        size = 0;\n+    }\n+    else if (cobj->type == BOOL) {\n+        size = LABRAD_BOOL_SIZE;\n+        if (cobj->i) *buf = TRUE;\n+        else *buf = FALSE;\n+    }\n+    else if (cobj->type == UINT31 || cobj->type == INT32) {\n+        size = LABRAD_INT_SIZE;\n+        ft_appropriate_memcpy(buf, &(cobj->i), size);\n+    }\n+    else if (cobj->type == UINT32) {\n+        size = LABRAD_INT_SIZE;\n+        ft_appropriate_memcpy(buf, &(cobj->n), size);\n+    }\n+    else if (cobj->type == FLOAT64 || cobj->type == FLOAT64_SU) {\n+        size = LABRAD_FLOAT_SIZE;\n+        ft_appropriate_memcpy(buf, &(cobj->x), size);\n+    }\n+    else if (cobj->type == COMPLEX128 || cobj->type == COMPLEX128_SU) {\n+        size = 2*LABRAD_FLOAT_SIZE;\n+        ft_appropriate_memcpy(buf, &(cobj->x), LABRAD_FLOAT_SIZE);\n+        ft_appropriate_memcpy(buf + LABRAD_FLOAT_SIZE, &(cobj->y), LABRAD_FLOAT_SIZE);\n+    }\n+    else if (cobj->type == STRING) {\n+        size = LABRAD_INT_SIZE + cobj->n;\n+        ft_appropriate_memcpy(buf, &(cobj->n), LABRAD_INT_SIZE);\n+        memcpy(buf + LABRAD_INT_SIZE, cobj->str, cobj->n);\n+    }\n+    else if (cobj->type == LIST || cobj->type == CLUSTER) {\n+        size = LABRAD_INT_SIZE;\n+        ft_appropriate_memcpy(buf, &(cobj->n), LABRAD_INT_SIZE);\n+        for (ui=0; ui<cobj->n; ui++) {\n+            size += ft_write_to_buf(cobj->cobj_arr[ui], buf+size);\n+        }\n+    }\n+    else if (cobj->type == NDARRAY || cobj->type == NDARRAY_SU) {\n+        size = 0;\n+        n = 1;\n+        for (ui=0; ui<cobj->n; ui++) {\n+            ft_appropriate_memcpy(buf+size, &(cobj->dim_arr[ui]), LABRAD_INT_SIZE);\n+            size += LABRAD_INT_SIZE;\n+            n *= cobj->dim_arr[ui];\n+        }\n+        if (cobj->subtype == BOOL) {\n+            memcpy(buf+size, cobj->arr, n*LABRAD_BOOL_SIZE);\n+        }\n+        else if (cobj->subtype == INT32) {\n+            for (i=0; i<n; i++) {\n+                ft_appropriate_memcpy(buf+size, &((int *)cobj->arr)[i], LABRAD_INT_SIZE);\n+                size += LABRAD_INT_SIZE;\n+            }\n+        }\n+        else if (cobj->subtype == UINT32) {\n+            for (i=0; i<n; i++) {\n+                ft_appropriate_memcpy(buf+size, &((unsigned int *)cobj->arr)[i], LABRAD_INT_SIZE);\n+                size += LABRAD_INT_SIZE;\n+            }\n+        }\n+        else if (cobj->subtype == FLOAT64) {\n+            for (i=0; i<n; i++) {\n+                ft_appropriate_memcpy(buf+size, &((double *)cobj->arr)[i], LABRAD_FLOAT_SIZE);\n+                size += LABRAD_FLOAT_SIZE;\n+            }\n+        }\n+        else if (cobj->subtype == COMPLEX128) {\n+            for (i=0; i<2*n; i++) {\n+                ft_appropriate_memcpy(buf+size, &((double *)cobj->arr)[i], LABRAD_FLOAT_SIZE);\n+                size += LABRAD_FLOAT_SIZE;\n+            }\n+        }\n+    }\n+    \n+    return size;\n+}\n+\n+void ft_appropriate_m","path":"labrad/fasttypes.c","position":2300,"original_position":2300,"commit_id":"36ba5ee3c782570ca6aab526846587a95abe3e33","original_commit_id":"36ba5ee3c782570ca6aab526846587a95abe3e33","user":{"login":"afowler","id":1313740,"avatar_url":"https://avatars.githubusercontent.com/u/1313740?v=3","gravatar_id":"","url":"https://api.github.com/users/afowler","html_url":"https://github.com/afowler","followers_url":"https://api.github.com/users/afowler/followers","following_url":"https://api.github.com/users/afowler/following{/other_user}","gists_url":"https://api.github.com/users/afowler/gists{/gist_id}","starred_url":"https://api.github.com/users/afowler/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/afowler/subscriptions","organizations_url":"https://api.github.com/users/afowler/orgs","repos_url":"https://api.github.com/users/afowler/repos","events_url":"https://api.github.com/users/afowler/events{/privacy}","received_events_url":"https://api.github.com/users/afowler/received_events","type":"User","site_admin":false},"body":"Thanks for the improvements!\nOn Apr 22, 2015 10:05 PM, \"ejeffrey\" <notifications@github.com> wrote:\n\n> In labrad/fasttypes.c\n> <https://github.com/martinisgroup/pylabrad/pull/88#discussion_r28936730>:\n>\n> > +                ft_appropriate_memcpy(buf+size, &((double *)cobj->arr)[i], LABRAD_FLOAT_SIZE);\n> > +                size += LABRAD_FLOAT_SIZE;\n> > +            }\n> > +        }\n> > +        else if (cobj->subtype == COMPLEX128) {\n> > +            for (i=0; i<2*n; i++) {\n> > +                ft_appropriate_memcpy(buf+size, &((double *)cobj->arr)[i], LABRAD_FLOAT_SIZE);\n> > +                size += LABRAD_FLOAT_SIZE;\n> > +            }\n> > +        }\n> > +    }\n> > +\n> > +    return size;\n> > +}\n> > +\n> > +void ft_appropriate_m\n>\n> tc_list needs an initializer, other wise if line 2303 causes an exception,\n> it will be use uninitialized in the exception handler.\n>\n> --\n> Reply to this email directly or view it on GitHub\n> <https://github.com/martinisgroup/pylabrad/pull/88/files#r28936730>.\n>\n","created_at":"2015-04-23T05:26:03Z","updated_at":"2015-04-23T05:26:03Z","html_url":"https://github.com/martinisgroup/pylabrad/pull/88#discussion_r28937188","pull_request_url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88","_links":{"self":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/comments/28937188"},"html":{"href":"https://github.com/martinisgroup/pylabrad/pull/88#discussion_r28937188"},"pull_request":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88"}}},"pull_request":{"url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88","id":33908577,"html_url":"https://github.com/martinisgroup/pylabrad/pull/88","diff_url":"https://github.com/martinisgroup/pylabrad/pull/88.diff","patch_url":"https://github.com/martinisgroup/pylabrad/pull/88.patch","issue_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues/88","number":88,"state":"open","locked":false,"title":"62 flatten unflatten c","user":{"login":"afowler","id":1313740,"avatar_url":"https://avatars.githubusercontent.com/u/1313740?v=3","gravatar_id":"","url":"https://api.github.com/users/afowler","html_url":"https://github.com/afowler","followers_url":"https://api.github.com/users/afowler/followers","following_url":"https://api.github.com/users/afowler/following{/other_user}","gists_url":"https://api.github.com/users/afowler/gists{/gist_id}","starred_url":"https://api.github.com/users/afowler/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/afowler/subscriptions","organizations_url":"https://api.github.com/users/afowler/orgs","repos_url":"https://api.github.com/users/afowler/repos","events_url":"https://api.github.com/users/afowler/events{/privacy}","received_events_url":"https://api.github.com/users/afowler/received_events","type":"User","site_admin":false},"body":"C flatten/unflatten is ready for testing and review. Minor differences to note (that can be changed), flatten returns a (string,string) tuple (the Python flatten returns labRAD types in the second position) and C unflatten takes this tuple as input (the Python unflatten takes the two tuple members as two separate inputs). This means you can write: unflatten(flatten([1,2,3])), and can also read the output of flatten([1,2,3]).\r\n\r\nLook forward to feedback!","created_at":"2015-04-22T22:38:19Z","updated_at":"2015-04-23T05:26:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b5435d334593d868d7741e679d9a49873c2d12c4","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88/commits","review_comments_url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88/comments","review_comment_url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/comments{/number}","comments_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues/88/comments","statuses_url":"https://api.github.com/repos/martinisgroup/pylabrad/statuses/36ba5ee3c782570ca6aab526846587a95abe3e33","head":{"label":"martinisgroup:62-flatten-unflatten-C","ref":"62-flatten-unflatten-C","sha":"36ba5ee3c782570ca6aab526846587a95abe3e33","user":{"login":"martinisgroup","id":5026004,"avatar_url":"https://avatars.githubusercontent.com/u/5026004?v=3","gravatar_id":"","url":"https://api.github.com/users/martinisgroup","html_url":"https://github.com/martinisgroup","followers_url":"https://api.github.com/users/martinisgroup/followers","following_url":"https://api.github.com/users/martinisgroup/following{/other_user}","gists_url":"https://api.github.com/users/martinisgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/martinisgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/martinisgroup/subscriptions","organizations_url":"https://api.github.com/users/martinisgroup/orgs","repos_url":"https://api.github.com/users/martinisgroup/repos","events_url":"https://api.github.com/users/martinisgroup/events{/privacy}","received_events_url":"https://api.github.com/users/martinisgroup/received_events","type":"Organization","site_admin":false},"repo":{"id":11461524,"name":"pylabrad","full_name":"martinisgroup/pylabrad","owner":{"login":"martinisgroup","id":5026004,"avatar_url":"https://avatars.githubusercontent.com/u/5026004?v=3","gravatar_id":"","url":"https://api.github.com/users/martinisgroup","html_url":"https://github.com/martinisgroup","followers_url":"https://api.github.com/users/martinisgroup/followers","following_url":"https://api.github.com/users/martinisgroup/following{/other_user}","gists_url":"https://api.github.com/users/martinisgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/martinisgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/martinisgroup/subscriptions","organizations_url":"https://api.github.com/users/martinisgroup/orgs","repos_url":"https://api.github.com/users/martinisgroup/repos","events_url":"https://api.github.com/users/martinisgroup/events{/privacy}","received_events_url":"https://api.github.com/users/martinisgroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/martinisgroup/pylabrad","description":"python interface for labrad","fork":false,"url":"https://api.github.com/repos/martinisgroup/pylabrad","forks_url":"https://api.github.com/repos/martinisgroup/pylabrad/forks","keys_url":"https://api.github.com/repos/martinisgroup/pylabrad/keys{/key_id}","collaborators_url":"https://api.github.com/repos/martinisgroup/pylabrad/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/martinisgroup/pylabrad/teams","hooks_url":"https://api.github.com/repos/martinisgroup/pylabrad/hooks","issue_events_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues/events{/number}","events_url":"https://api.github.com/repos/martinisgroup/pylabrad/events","assignees_url":"https://api.github.com/repos/martinisgroup/pylabrad/assignees{/user}","branches_url":"https://api.github.com/repos/martinisgroup/pylabrad/branches{/branch}","tags_url":"https://api.github.com/repos/martinisgroup/pylabrad/tags","blobs_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/refs{/sha}","trees_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/trees{/sha}","statuses_url":"https://api.github.com/repos/martinisgroup/pylabrad/statuses/{sha}","languages_url":"https://api.github.com/repos/martinisgroup/pylabrad/languages","stargazers_url":"https://api.github.com/repos/martinisgroup/pylabrad/stargazers","contributors_url":"https://api.github.com/repos/martinisgroup/pylabrad/contributors","subscribers_url":"https://api.github.com/repos/martinisgroup/pylabrad/subscribers","subscription_url":"https://api.github.com/repos/martinisgroup/pylabrad/subscription","commits_url":"https://api.github.com/repos/martinisgroup/pylabrad/commits{/sha}","git_commits_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/commits{/sha}","comments_url":"https://api.github.com/repos/martinisgroup/pylabrad/comments{/number}","issue_comment_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues/comments{/number}","contents_url":"https://api.github.com/repos/martinisgroup/pylabrad/contents/{+path}","compare_url":"https://api.github.com/repos/martinisgroup/pylabrad/compare/{base}...{head}","merges_url":"https://api.github.com/repos/martinisgroup/pylabrad/merges","archive_url":"https://api.github.com/repos/martinisgroup/pylabrad/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/martinisgroup/pylabrad/downloads","issues_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues{/number}","pulls_url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls{/number}","milestones_url":"https://api.github.com/repos/martinisgroup/pylabrad/milestones{/number}","notifications_url":"https://api.github.com/repos/martinisgroup/pylabrad/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/martinisgroup/pylabrad/labels{/name}","releases_url":"https://api.github.com/repos/martinisgroup/pylabrad/releases{/id}","created_at":"2013-07-16T22:08:52Z","updated_at":"2015-04-15T23:34:57Z","pushed_at":"2015-04-22T22:38:19Z","git_url":"git://github.com/martinisgroup/pylabrad.git","ssh_url":"git@github.com:martinisgroup/pylabrad.git","clone_url":"https://github.com/martinisgroup/pylabrad.git","svn_url":"https://github.com/martinisgroup/pylabrad","homepage":"","size":7600,"stargazers_count":5,"watchers_count":5,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":28,"forks":1,"open_issues":28,"watchers":5,"default_branch":"master"}},"base":{"label":"martinisgroup:master","ref":"master","sha":"21762c16679fe3d5f52df8c2dd13f9e899cb58df","user":{"login":"martinisgroup","id":5026004,"avatar_url":"https://avatars.githubusercontent.com/u/5026004?v=3","gravatar_id":"","url":"https://api.github.com/users/martinisgroup","html_url":"https://github.com/martinisgroup","followers_url":"https://api.github.com/users/martinisgroup/followers","following_url":"https://api.github.com/users/martinisgroup/following{/other_user}","gists_url":"https://api.github.com/users/martinisgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/martinisgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/martinisgroup/subscriptions","organizations_url":"https://api.github.com/users/martinisgroup/orgs","repos_url":"https://api.github.com/users/martinisgroup/repos","events_url":"https://api.github.com/users/martinisgroup/events{/privacy}","received_events_url":"https://api.github.com/users/martinisgroup/received_events","type":"Organization","site_admin":false},"repo":{"id":11461524,"name":"pylabrad","full_name":"martinisgroup/pylabrad","owner":{"login":"martinisgroup","id":5026004,"avatar_url":"https://avatars.githubusercontent.com/u/5026004?v=3","gravatar_id":"","url":"https://api.github.com/users/martinisgroup","html_url":"https://github.com/martinisgroup","followers_url":"https://api.github.com/users/martinisgroup/followers","following_url":"https://api.github.com/users/martinisgroup/following{/other_user}","gists_url":"https://api.github.com/users/martinisgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/martinisgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/martinisgroup/subscriptions","organizations_url":"https://api.github.com/users/martinisgroup/orgs","repos_url":"https://api.github.com/users/martinisgroup/repos","events_url":"https://api.github.com/users/martinisgroup/events{/privacy}","received_events_url":"https://api.github.com/users/martinisgroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/martinisgroup/pylabrad","description":"python interface for labrad","fork":false,"url":"https://api.github.com/repos/martinisgroup/pylabrad","forks_url":"https://api.github.com/repos/martinisgroup/pylabrad/forks","keys_url":"https://api.github.com/repos/martinisgroup/pylabrad/keys{/key_id}","collaborators_url":"https://api.github.com/repos/martinisgroup/pylabrad/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/martinisgroup/pylabrad/teams","hooks_url":"https://api.github.com/repos/martinisgroup/pylabrad/hooks","issue_events_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues/events{/number}","events_url":"https://api.github.com/repos/martinisgroup/pylabrad/events","assignees_url":"https://api.github.com/repos/martinisgroup/pylabrad/assignees{/user}","branches_url":"https://api.github.com/repos/martinisgroup/pylabrad/branches{/branch}","tags_url":"https://api.github.com/repos/martinisgroup/pylabrad/tags","blobs_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/refs{/sha}","trees_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/trees{/sha}","statuses_url":"https://api.github.com/repos/martinisgroup/pylabrad/statuses/{sha}","languages_url":"https://api.github.com/repos/martinisgroup/pylabrad/languages","stargazers_url":"https://api.github.com/repos/martinisgroup/pylabrad/stargazers","contributors_url":"https://api.github.com/repos/martinisgroup/pylabrad/contributors","subscribers_url":"https://api.github.com/repos/martinisgroup/pylabrad/subscribers","subscription_url":"https://api.github.com/repos/martinisgroup/pylabrad/subscription","commits_url":"https://api.github.com/repos/martinisgroup/pylabrad/commits{/sha}","git_commits_url":"https://api.github.com/repos/martinisgroup/pylabrad/git/commits{/sha}","comments_url":"https://api.github.com/repos/martinisgroup/pylabrad/comments{/number}","issue_comment_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues/comments{/number}","contents_url":"https://api.github.com/repos/martinisgroup/pylabrad/contents/{+path}","compare_url":"https://api.github.com/repos/martinisgroup/pylabrad/compare/{base}...{head}","merges_url":"https://api.github.com/repos/martinisgroup/pylabrad/merges","archive_url":"https://api.github.com/repos/martinisgroup/pylabrad/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/martinisgroup/pylabrad/downloads","issues_url":"https://api.github.com/repos/martinisgroup/pylabrad/issues{/number}","pulls_url":"https://api.github.com/repos/martinisgroup/pylabrad/pulls{/number}","milestones_url":"https://api.github.com/repos/martinisgroup/pylabrad/milestones{/number}","notifications_url":"https://api.github.com/repos/martinisgroup/pylabrad/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/martinisgroup/pylabrad/labels{/name}","releases_url":"https://api.github.com/repos/martinisgroup/pylabrad/releases{/id}","created_at":"2013-07-16T22:08:52Z","updated_at":"2015-04-15T23:34:57Z","pushed_at":"2015-04-22T22:38:19Z","git_url":"git://github.com/martinisgroup/pylabrad.git","ssh_url":"git@github.com:martinisgroup/pylabrad.git","clone_url":"https://github.com/martinisgroup/pylabrad.git","svn_url":"https://github.com/martinisgroup/pylabrad","homepage":"","size":7600,"stargazers_count":5,"watchers_count":5,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":28,"forks":1,"open_issues":28,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88"},"html":{"href":"https://github.com/martinisgroup/pylabrad/pull/88"},"issue":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/issues/88"},"comments":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/issues/88/comments"},"review_comments":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88/comments"},"review_comment":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/pulls/88/commits"},"statuses":{"href":"https://api.github.com/repos/martinisgroup/pylabrad/statuses/36ba5ee3c782570ca6aab526846587a95abe3e33"}}}}