From 564b3eebb5c4cd346cca496a7f5d5f8fa19bbca0 Mon Sep 17 00:00:00 2001
From: Christian Zimmermann <christian.zimmermann@ur.de>
Date: Tue, 6 Feb 2024 01:41:49 +0100
Subject: [PATCH] WIP...

---
 cnorxz/core/include/array_wrapper.h |  59 +++++++++++++++-
 cnorxz/core/lib/array_wrapper.cpp   | 101 ++++++++++++++++++++++++++--
 cnorxz/core/lib/range_wrapper.cpp   |   2 +
 setup.py                            |   7 +-
 4 files changed, 160 insertions(+), 9 deletions(-)

diff --git a/cnorxz/core/include/array_wrapper.h b/cnorxz/core/include/array_wrapper.h
index fb553fc..a13ee9f 100644
--- a/cnorxz/core/include/array_wrapper.h
+++ b/cnorxz/core/include/array_wrapper.h
@@ -4,6 +4,8 @@
 
 #include <Python.h>
 #include "cnorxz.h"
+#include "cereal/cnorxz_cereal.h"
+#include "numpy/ndarraytypes.h"
 
 namespace CNORXZ
 {
@@ -18,9 +20,42 @@ namespace CNORXZ
 	virtual SizeT size() const = 0;
 	// virtual PyObject* get() const = 0; // operator[]!!
 #ifdef HAVE_CEREAL
-	virtual void writeFile(Format f, const String& fname);
-	virtual void readFile(Format f, const String& fname);
+	virtual void writeFile(cer::Format f, const String& fname) const = 0;
+	virtual void readFile(cer::Format f, const String& fname) const = 0;
 #endif
+	virtual int typenum() const = 0;
+	virtual const void* data() const = 0;
+	virtual SizeT datasize() const = 0;
+    };
+
+    template <typename T>
+    struct Typenum
+    {
+	static const int value = NPY_NOTYPE;
+    };
+
+    template <>
+    struct Typenum<Double>
+    {
+	static const int value = NPY_DOUBLE;
+    };
+
+    template <>
+    struct Typenum<Int>
+    {
+	static const int value = NPY_INT32;
+    };
+
+    template <>
+    struct Typenum<LInt>
+    {
+	static const int value = NPY_INT64;
+    };
+
+    template <>
+    struct Typenum<SizeT>
+    {
+	static const int value = NPY_UINT64;
     };
 
     template <typename T>
@@ -31,11 +66,29 @@ namespace CNORXZ
 	    
     public:
 	CArrayWrapper() : mArr( std::make_shared<MArray<T>>() ) {}
-	//DEFAULT_MEMBERS(CArrayWrapper);
 	CArrayWrapper(const RangePtr& r) : mArr( std::make_shared<MArray<T>>(r) ) {}
 
 	virtual RangePtr range() const override final { return mArr->range(); }
 	virtual SizeT size() const override final { return mArr->size(); }
+#ifdef HAVE_CEREAL
+	virtual void writeFile(cer::Format f, const String& fname) const override final
+	{
+	    if(f == cer::Format::XML) { cer::writeXMLFile(fname, *mArr); }
+	    else if(f == cer::Format::JSON) { cer::writeJSONFile(fname, *mArr); }
+	    else { cer::writeBINARYFile(fname, *mArr); }
+	}
+	
+	virtual void readFile(cer::Format f, const String& fname) const override final
+	{
+	    if(f == cer::Format::XML) { cer::readXMLFile(fname, *mArr); }
+	    else if(f == cer::Format::JSON) { cer::readJSONFile(fname, *mArr); }
+	    else { cer::readBINARYFile(fname, *mArr); }
+	}
+#endif
+	virtual int typenum() const override final { return Typenum<T>::value; }
+	virtual const void* data() const override final
+	{ return reinterpret_cast<const void*>( mArr->data() ); }
+	virtual SizeT datasize() const override final { return size() * sizeof(T); }
     };
 	
 }
diff --git a/cnorxz/core/lib/array_wrapper.cpp b/cnorxz/core/lib/array_wrapper.cpp
index c7ba2b1..5957a44 100644
--- a/cnorxz/core/lib/array_wrapper.cpp
+++ b/cnorxz/core/lib/array_wrapper.cpp
@@ -1,13 +1,15 @@
 
 #include "array_wrapper.h"
+#include "numpy/arrayobject.h"
 
 using namespace CNORXZ;
+using namespace CNORXZ::cer;
 
 int PyCArrayB_init(PyCArrayB* self, PyObject* args, PyObject* kwds)
 {
     static char* kwlist[] = { "type", "extension" , NULL };
     SizeT ext = 0;
-    const char* type;
+    char* type = NULL;
 
     if(not PyArg_ParseTupleAndKeywords(args, kwds, "s|k", kwlist, &type, &ext)){
 	return -1;
@@ -37,12 +39,103 @@ PyObject* PyCArrayB_size(PyCArrayB* self)
 	return NULL;
     }
     const SizeT retval = self->ptrObj->size();
-    return Py_BuildValue("k",retval);
+    return Py_BuildValue("k", retval);
+}
+
+PyObject* PyCArrayB_range(PyCArrayB* self)
+{
+    if(not self->ptrObj->range()){
+	PyErr_SetString(PyExc_RuntimeError, "array not initialized");
+	return NULL;
+    }
+    const RangePtr retval = self->ptrObj->range();
+    return Py_BuildValue("k", retval);
+}
+
+Format formatFromString(const char* fstr)
+{
+    Format f = Format::BINARY;
+    if(fstr != NULL){
+	if(strcmp(fstr,"binary") == 0){
+	    f = Format::BINARY;
+	}
+	else if(strcmp(fstr,"json") == 0){
+	    f = Format::JSON;
+	}
+	else if(strcmp(fstr,"xml") == 0){
+	    f = Format::XML;
+	}
+	else {
+	    PyErr_SetString(PyExc_RuntimeError, "unknown format");
+	}
+    }
+    return f;
+}
+
+void PyCArrayB_writeFile(PyCArrayB* self, PyObject* args, PyObject* kwds)
+{
+    if(not self->ptrObj->range()){
+	PyErr_SetString(PyExc_RuntimeError, "array not initialized");
+    }
+    static char* kwlist[] = { "format", "filename" , NULL };
+    char* fname = NULL;
+    char* format = NULL;
+
+    if(not PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &fname, &format)){
+	return;
+    }
+    const String fn(fname);
+    const Format f = formatFromString(format);
+    self->ptrObj->writeFile(f, fn);
+}
+
+void PyCArrayB_readFile(PyCArrayB* self, PyObject* args, PyObject* kwds)
+{
+    static char* kwlist[] = { "format", "filename" , NULL };
+    char* fname = NULL;
+    char* format = NULL;
+
+    if(not PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &fname, &format)){
+	return;
+    }
+    const String fn(fname);
+    const Format f = formatFromString(format);
+    self->ptrObj->readFile(f, fn);
+}
+
+PyObject* PyCArrayB_npa(PyCArrayB* self)
+{
+    // return viewing (!) numpy array
+    if(not self->ptrObj->range()){
+	PyErr_SetString(PyExc_RuntimeError, "array not initialized");
+	return NULL;
+    }
+    const SizeT d = self->ptrObj->range()->dim();
+    std::vector<npy_intp> dims(d);
+    /*
+    Py_INCREF(self); // keep data alive
+    PyObject* o = PyArray_SimpleNewFromData
+	( static_cast<int>( d ) , dims.data(), self->ptrObj->typenum(), self->ptrObj->data() );
+    PyArray_BASE(o) = (PyObject*) self;
+    */
+    PyObject* o = PyArray_SimpleNew
+	( static_cast<int>( d ) , dims.data(), self->ptrObj->typenum() );
+    void* data = PyArray_DATA(o);
+    const npy_intp s = PyArray_NBYTES(o);
+    if(s != self->ptrObj->datasize()) {
+	PyErr_SetString(PyExc_RuntimeError, "lib error");
+    }
+    memcpy( data, self->ptrObj->data(), self->ptrObj->datasize() );
+    return o;
 }
 
 PyMethodDef PyCArrayB_methods[] = {
-    { "size", (PyCFunction) PyCArrayB_size, METH_VARARGS, "return size of the array" },
-    { NULL }
+    { "size", (PyCFunction) PyCArrayB_size, METH_VARARGS| METH_KEYWORDS, "return size of the array" },
+    { "range", (PyCFunction) PyCArrayB_range, METH_VARARGS| METH_KEYWORDS, "return array range" },
+    { "writeFile", (PyCFunction) PyCArrayB_writeFile, METH_VARARGS| METH_KEYWORDS, "write array to file" },
+    { "readFile", (PyCFunction) PyCArrayB_readFile, METH_VARARGS| METH_KEYWORDS, "read array from file" },
+    { "npa", (PyCFunction) PyCArrayB_npa, METH_VARARGS| METH_KEYWORDS, "return numpy array (view)" },
+    { NULL, NULL, 0, NULL }
 };
 
 PyTypeObject PyCArrayBType = { PyVarObject_HEAD_INIT(NULL,0) "cnorxz.CArray" };
diff --git a/cnorxz/core/lib/range_wrapper.cpp b/cnorxz/core/lib/range_wrapper.cpp
index 184a886..38626a0 100644
--- a/cnorxz/core/lib/range_wrapper.cpp
+++ b/cnorxz/core/lib/range_wrapper.cpp
@@ -59,6 +59,8 @@ PyTypeObject* PyCRangeType_init()
     PyCRangeType.tp_methods = PyCRange_methods;
     PyCRangeType.tp_init = (initproc) PyCRange_init;
 
+    import_array();
+    
     if(PyType_Ready(&PyCRangeType) < 0){
 	return NULL;
     }
diff --git a/setup.py b/setup.py
index 726b3ab..48b3b9f 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 
-import Cython.Build
-#import setuptools
+#import numpy
+import setuptools
 from distutils.core import setup, Extension
 import sysconfig
 import subprocess
@@ -27,6 +27,8 @@ inc_dirs = list()
 if path_to_cnorxz != "":
     inc_dirs.append( path_to_cnorxz + "/include/cnorxz" )
 inc_dirs.append( "cnorxz/core/include" )
+#inc_dirs.append( numpy.get_include() )
+inc_dirs.append( "/home/chizeta/my_env/lib/python3.11/site-packages/numpy/core/include" )
     
 lib_dirs = list()
 lib_dirs.append( "/usr/lib" )
@@ -37,6 +39,7 @@ lib_dirs.append( "/home/chizeta/repos/cnorxz/install/lib" )
 
 extra_compile_args = sysconfig.get_config_var('CFLAGS').split()
 cnorxz_flags = subprocess.run([path_to_cnorxz+"/bin/cnorxz-config",'--flags'],stdout=subprocess.PIPE).stdout.decode('ascii').split()
+cnorxz_flags.remove("-Werror")
 extra_compile_args += cnorxz_flags
 
 default_extension_args = dict(