@cython.boundscheck(False)
@cython.wraparound(False)
# ...
cdef class MURGE:
    """

    """

    # .................................
    Returns         = MURGEReturns
    IParam          = MURGEIParam
    RParam          = MURGERParam
    IInfo           = MURGEIInfo
    RInfo           = MURGERInfo
    AssemblyMode    = MURGEAssemblyMode
    AssemblyOp      = MURGEAssemblyOp
    Solver          = MURGESolver
    Boolean         = MURGEBoolean    
    NormRules       = MURGENormRules
    ScalModes       = MURGEScalModes
    # .................................

    # .................................
    cdef:
        object nmatrices
        object Initialized
    # .................................

    # .................................
    def __cinit__(self, nmatrices = 1):
        self.nmatrices = nmatrices
        self.Initialized = False 
        CHKERR(MURGE_Initialize(nmatrices))
        self.Initialized = True

    def __dealloc__(self):
        if self.Initialized:
            CHKERR(MURGE_Finalize())
            self.Initialized = False 
    # .................................

    # .................................
    def SetDefaultOptions(self, id, stratnum):
        CHKERR(MURGE_SetDefaultOptions(id, stratnum))

    def SetOptionInt(self, id, number, value):
        CHKERR(MURGE_SetOptionINT(id, number, value))

    def SetOptionReal(self, id, number, value):
        CHKERR(MURGE_SetOptionREAL(id, number, value))

    def SetCommunicator(self, id, MPI.Comm comm not None):
        cdef MPI_Comm c_comm = comm.ob_mpi
        CHKERR(MURGE_SetCommunicator(id, c_comm))
    # .................................

    # .................................
    def GraphBegin(self, id, N, edgenbr):
        CHKERR(MURGE_GraphBegin(id, N, edgenbr))        

    def GraphEdge(self, id, col, row):
        CHKERR(MURGE_GraphEdge(id, col, row))

    def GraphEnd(self, id):
        CHKERR(MURGE_GraphEnd(id))

    def GraphGlobalCSR(self, id, np.ndarray[np_INTL,ndim=1] rowptr,
                       np.ndarray[np_INTS,ndim=1] cols, root=-1):
        n = len(rowptr)
        CHKERR(MURGE_GraphGlobalCSR(id, n, <INTL*> rowptr.data, <INTS*> cols.data, root))

    def GraphGlobalCSC(self, id, np.ndarray[np_INTL,ndim=1]
                       colptr,np.ndarray[np_INTS,ndim=1] rows, root=-1):
        n = len(colptr)
        CHKERR(MURGE_GraphGlobalCSC(id, n, <INTL*> colptr.data, <INTS*> rows.data, root))

    def GraphGlobalIJV(self, id, n, np.ndarray[np_INTS,ndim=1] rows,
                       np.ndarray[np_INTS,ndim=1] cols, root=-1):
        nnz = len(rows)
        CHKERR(MURGE_GraphGlobalIJV(id, n, nnz, <INTS*> rows.data, <INTS*> cols.data, root))
    # .................................

    # .................................
    def Save(self, id, char* directory):
        CHKERR(MURGE_Save(id, directory))

    def Load(self, id, char* directory):
        CHKERR(MURGE_Load(id, directory))
    # .................................

    # .................................
    def GetLocalNodeNbr(self, id, np.ndarray[np_INTS,ndim=1] nodenbr):
        CHKERR(MURGE_GetLocalNodeNbr(id, <INTS*> nodenbr.data))

    def GetLocalNodeList(self, id, np.ndarray[np_INTS,ndim=1] nodelist):
        CHKERR(MURGE_GetLocalNodeList(id, <INTS*> nodelist.data))

    def GetLocalUnknownNbr(self, id, np.ndarray[np_INTS,ndim=1] unkownnbr):
        CHKERR(MURGE_GetLocalUnknownNbr(id, <INTS*> unkownnbr.data))

    def GetLocalUnknownList(self, id, np.ndarray[np_INTS,ndim=1] unkownlist):
        CHKERR(MURGE_GetLocalUnknownList(id, <INTS*> unkownlist.data))
    # .................................

    # .................................
    def AssemblyBegin(self, id, n, coefnbr, op, op2, mode, sym):
        CHKERR(MURGE_AssemblyBegin(id, n, coefnbr, op, op2, mode, sym))

    def AssemblySetValue(self, id, row, col, value):
        CHKERR(MURGE_AssemblySetValue(id, row, col, value))

    def AssemblySetNodeValues(self, id, row, col, np.ndarray[np.double_t,ndim=1] values):
        CHKERR(MURGE_AssemblySetNodeValues(id, row, col, <COEF*> values.data))

    def AssemblySetBlockValues(  self, id 
                               , np.ndarray[np_INTS,ndim=1] rows 
                               , np.ndarray[np_INTS,ndim=1] cols 
                               , np.ndarray[np.double_t,ndim=1] values):
        nrow = len(rows)
        ncol = len(cols)
        CHKERR(MURGE_AssemblySetBlockValues(id, nrow, <INTS*> rows.data,
                                            ncol, <INTS*> cols.data, <COEF*>
                                            values.data))

    def AssemblyEnd(self, id):
        CHKERR(MURGE_AssemblyEnd(id))

    def MatrixReset(self, id):
        CHKERR(MURGE_MatrixReset(id))

    def MatrixGlobalCSR(self, id, np.ndarray[np_INTL,ndim=1] rowptr,
                        np.ndarray[np_INTS,ndim=1] cols, np.ndarray[np.double_t,ndim=1] values, root, op, sym):
        n = len(rowptr)
        CHKERR(MURGE_MatrixGlobalCSR(id, n,<INTL*> rowptr.data, <INTS*> cols.data, <COEF*> values.data, root, op, sym))

    def MatrixGlobalCSC(self, id, np.ndarray[np_INTL,ndim=1] colptr,
                        np.ndarray[np_INTS,ndim=1] rows, np.ndarray[np.double_t,ndim=1] values, root, op, sym):
        n = len(colptr)
        CHKERR(MURGE_MatrixGlobalCSC(id, n,<INTL*> colptr.data, <INTS*> rows.data, <COEF*> values.data, root, op, sym))

    def MatrixGlobalIJV(self, id, n, np.ndarray[np_INTS,ndim=1] rows,
                        np.ndarray[np_INTS,ndim=1] cols, np.ndarray[np.double_t,ndim=1] values, root, op, sym):
        nnz = len(rows)
        CHKERR(MURGE_MatrixGlobalIJV(id, n, nnz, <INTS*> rows.data, <INTS*>
                                         cols.data, <COEF*> values.data, root, op, sym))
    # .................................

    # .................................    
    def SetGlobalRHS(self, id, np.ndarray[np.double_t,ndim=1] b, root, op):
        CHKERR(MURGE_SetGlobalRHS(id, <COEF*> b.data, root, op))

    def SetLocalRHS(self, id, np.ndarray[np.double_t,ndim=1] b, op, op2):
        CHKERR(MURGE_SetLocalRHS(id, <COEF*> b.data, op, op2))

    def SetRHS(self, id, n, np.ndarray[np_INTS,ndim=1] coefsidx,
               np.ndarray[np.double_t,ndim=1] b, op, op2, mode):
        CHKERR(MURGE_SetRHS(id, n, <INTS*> coefsidx.data, <COEF*> b.data, op, op2, mode))

    def RHSReset(self, id):
        CHKERR(MURGE_RHSReset(id))
    # .................................

    # .................................    
    def GetGlobalSolution(self, id, np.ndarray[np.double_t,ndim=1] x, root):
        CHKERR(MURGE_GetGlobalSolution(id, <COEF*> x.data, root))

    def GetLocalSolution(self, id, np.ndarray[np.double_t,ndim=1] x):
        CHKERR(MURGE_GetLocalSolution(id, <COEF*> x.data))

    def GetSolution(self, id, n, np.ndarray[np_INTS,ndim=1] coefsidx,
               np.ndarray[np.double_t,ndim=1] x, mode):
        CHKERR(MURGE_GetSolution(id, n, <INTS*> coefsidx.data, <COEF*> x.data, mode))
    # .................................

    # .................................    
    def GetGlobalNorm(self, id, np.ndarray[np.double_t,ndim=1] norm, root, rule):
        CHKERR(MURGE_GetGlobalNorm(id, <REAL*> norm.data, root, rule))

    def GetLocalNorm(self, id, np.ndarray[np.double_t,ndim=1] norm, rule):
        CHKERR(MURGE_GetLocalNorm(id, <REAL*> norm.data, rule))

    def GetNorm(self, id, n, np.ndarray[np_INTS,ndim=1] coefsidx,
               np.ndarray[np.double_t,ndim=1] norm, rule, mode):
        CHKERR(MURGE_GetNorm(id, n, <INTS*> coefsidx.data, <COEF*> norm.data, rule, mode))

    def ApplyGlobalScaling(self, id, np.ndarray[np.double_t,ndim=1] scal, root,
                          sc_mode):
        CHKERR(MURGE_ApplyGlobalScaling(id, <REAL*> scal.data, root, sc_mode))

    def ApplyLocalScaling(self, id, np.ndarray[np.double_t,ndim=1] scal,
                          sc_mode):
        CHKERR(MURGE_ApplyLocalScaling(id, <REAL*> scal.data, sc_mode))

    def ApplyScaling(self, id, n, np.ndarray[np_INTS,ndim=1] coefsidx,
               np.ndarray[np.double_t,ndim=1] scal, sc_mode, mode):
        CHKERR(MURGE_ApplyScaling(id, n, <INTS*> coefsidx.data, <COEF*> scal.data, sc_mode, mode))
    # .................................   

    # .................................    
    def Clean(self, id):
        CHKERR(MURGE_Clean(id))

    def GetSolver(self, np.ndarray[np_INTS,ndim=1] solver):
        CHKERR(MURGE_GetSolver(<INTS*> solver.data))
    # .................................   

    # .................................    
#    def GetInfoInt(self, id, metric, np.ndarray[np_INTS,ndim=1] values):
    def GetInfoInt(self, id, metric, np.ndarray[np_INTL,ndim=1] values):
        CHKERR(MURGE_GetInfoINT(id, metric, <INTL*> values.data))

    def GetInfoReal(self, id, metric, np.ndarray[np.double_t,ndim=1] values):
        CHKERR(MURGE_GetInfoREAL(id, metric, <COEF*> values.data))

    def PrintError(self, error_number):
        CHKERR(MURGE_PrintError(error_number))

    def ExitOnError(self, error_number):
        CHKERR(MURGE_ExitOnError(error_number))
    # .................................

    # .................................    
    def GetGlobalProduct(self, id, np.ndarray[np.double_t,ndim=1] x, root=-1):
        CHKERR(MURGE_GetGlobalProduct(id, <COEF*> x.data, root))

    def GetLocalProduct(self, id, np.ndarray[np.double_t,ndim=1] x):
        CHKERR(MURGE_GetLocalProduct(id, <COEF*> x.data))
    # .................................

    # .................................    
    def Analyze(self, id):
        CHKERR(MURGE_Analyze(id))

    def Factorize(self, id):
        CHKERR(MURGE_Factorize(id))

#   TODO : the following function is not defined 
#    def SetOrdering(self, id, np.ndarray[np_INTS,ndim=1] permutation): 
#        CHKERR(MURGE_SetOrdering(id, <INTS*> permutation.data))

    def ProductSetLocalNodeNbr(self, id, n):
        CHKERR(MURGE_ProductSetLocalNodeNbr(id, n))

    def ProductSetGlobalNodeNbr(self, id, n):
        CHKERR(MURGE_ProductSetGlobalNodeNbr(id, n))

    def ProductSetLocalNodeList(self, id, np.ndarray[np_INTS,ndim=1] l2g):
        CHKERR(MURGE_ProductSetLocalNodeList(id, <INTS*> l2g.data))

    def ForceNoFacto(self, id):
        CHKERR(MURGE_ForceNoFacto(id))

    def SetLocalNodeList(self, id, n, np.ndarray[np_INTS,ndim=1] lst): 
        CHKERR(MURGE_SetLocalNodeList(id, n, <INTS*> lst.data))

    def AssemblySetSequence(self, id, coefnbr, np.ndarray[np_INTS,ndim=1]
                            rows, np.ndarray[np_INTS,ndim=1] cols, op, op2,
                            modes, nodes, np.ndarray[np_INTS,ndim=1] id_seq):
        CHKERR(MURGE_AssemblySetSequence(id, coefnbr, <INTS*> rows.data, <INTS*>
                                         cols.data, op, op2, modes, nodes,
                                         <INTS*> id_seq.data))

    def AssemblyUseSequence(self, id, id_seq, np.ndarray[np.double_t,ndim=1]
                            values): 
        CHKERR(MURGE_AssemblyUseSequence(id, id_seq, <COEF*> values.data))

    def AssemblyDeleteSequence(self, id, id_seq):
        CHKERR(MURGE_AssemblyDeleteSequence(id, id_seq))
    # .................................    

# --------------------------------------------------------------------
del MURGEReturns
del MURGEIParam
del MURGERParam
del MURGEIInfo
del MURGERInfo
del MURGEAssemblyMode
del MURGEAssemblyOp
del MURGESolver
del MURGEBoolean    
del MURGENormRules
del MURGEScalModes
# --------------------------------------------------------------------

