//------------------------------------------------------------------------------
// GrB_Matrix_import: import a matrix in CSR, CSC, FullC, FullR, or COO format
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//------------------------------------------------------------------------------

// No typecasting is done.  The type of entries in the Ax array must match
// the GrB_Type type parameter.

#include "GB_export.h"
#include "GB_build.h"

#define GB_FREE_ALL                 \
{                                   \
    GB_Matrix_free (A) ;            \
    GB_FREE (&Ap_copy, Ap_size) ;   \
    GB_FREE (&Ai_copy, Ai_size) ;   \
    GB_FREE (&Ax_copy, Ax_size) ;   \
}

//------------------------------------------------------------------------------
// GB_import_worker: import a matrix of any type
//------------------------------------------------------------------------------

static GrB_Info GB_import_worker   // import a matrix of any type
(
    GrB_Matrix *A,          // handle of matrix to create
    GrB_Type type,          // type of matrix to create
    GrB_Index nrows,        // number of rows of the matrix
    GrB_Index ncols,        // number of columns of the matrix
    const GrB_Index *Ap,    // pointers for CSR, CSC, row indices for COO
    const GrB_Index *Ai,    // row indices for CSR, CSC, col indices for COO
    const void *Ax,         // values (must match the GrB_Type type parameter)
    GrB_Index Ap_len,       // number of entries in Ap (not # of bytes)
    GrB_Index Ai_len,       // number of entries in Ai (not # of bytes)
    GrB_Index Ax_len,       // number of entries in Ax (not # of bytes)
    GrB_Format format,      // import format
    GB_Context Context
)
{ 

    //--------------------------------------------------------------------------
    // check inputs
    //--------------------------------------------------------------------------

    GB_RETURN_IF_NULL (A) ;
    (*A) = NULL ;
    GB_RETURN_IF_NULL (Ax) ;
    ASSERT_TYPE_OK (type, "type for GrB_Matrix_import", GB0) ;
    GrB_Info info ;

    // GrB_Matrix_import has no descritptor so it only supports a secure import
    bool fast_import = false ;

    if (nrows > GB_NMAX || ncols > GB_NMAX || Ap_len > GB_NMAX
        || Ai_len > GB_NMAX || Ax_len > GB_NMAX)
    { 
        // problem is too large
        return (GrB_INVALID_VALUE) ;
    }

    GrB_Index nvals = 0 ;
    bool ok = true ;
    int64_t plen = (format == GrB_CSR_FORMAT) ? (nrows+1) : (ncols+1) ;

    switch (format)
    {

        case GrB_CSR_FORMAT :
        case GrB_CSC_FORMAT :

            GB_RETURN_IF_NULL (Ap) ;
            GB_RETURN_IF_NULL (Ai) ;
            if (Ap_len < plen)
            { 
                // Ap is too small
                return (GrB_INVALID_VALUE) ;
            }
            nvals = Ap [plen-1] ;
            if (Ai_len < nvals || Ax_len < nvals || nvals > GB_NMAX)
            { 
                // Ai and/or Ax are too small or problem is too large
                return (GrB_INVALID_VALUE) ;
            }
            break ;

//      case GrB_DENSE_ROW_FORMAT :
//      case GrB_DENSE_COL_FORMAT :
//
//          ok = GB_int64_multiply (&nvals, (int64_t) nrows, (int64_t) ncols) ;
//          if (!ok || Ax_len < nvals)
//          {
//              // Ap, Ai, and Ax must all have the same size
//              return (GrB_INVALID_VALUE) ;
//          }
//          break ;

        case GrB_COO_FORMAT :

            GB_RETURN_IF_NULL (Ap) ;
            GB_RETURN_IF_NULL (Ai) ;
            nvals = Ap_len ;
            if (Ai_len != nvals || Ax_len != nvals)
            { 
                // Ap, Ai, and Ax must all have the same size
                return (GrB_INVALID_VALUE) ;
            }
            break ;

        default :

            // unknown format
            return (GrB_INVALID_VALUE) ;
    }

    //--------------------------------------------------------------------------
    // allocate copies of Ap, Ai, and Ax to be imported
    //--------------------------------------------------------------------------

    GrB_Index *Ap_copy = NULL ; size_t Ap_size = 0 ;
    GrB_Index *Ai_copy = NULL ; size_t Ai_size = 0 ;
    GB_void   *Ax_copy = NULL ; size_t Ax_size = 0 ;
    size_t typesize = type->size ;

    // Ap_copy, Ai_copy, Ax_copy are GB_MALLOC'ed so they are already in the
    // debug memtable.  Thus, GB_import does not add them again to the
    // memtable (with add_to_memtable set to false).

    switch (format)
    {
        case GrB_CSR_FORMAT : 
        case GrB_CSC_FORMAT : 
            Ap_copy = GB_MALLOC (plen,           GrB_Index, &Ap_size) ;
            Ai_copy = GB_MALLOC (nvals,          GrB_Index, &Ai_size) ;
            Ax_copy = GB_MALLOC (nvals*typesize, GB_void,   &Ax_size) ; // x:OK
            ok = (Ap_copy != NULL && Ai_copy != NULL && Ax_copy != NULL) ;
            break ;

//      case GrB_DENSE_ROW_FORMAT :
//      case GrB_DENSE_COL_FORMAT :
//          Ax_copy = GB_MALLOC (nvals*typesize, GB_void,   &Ax_size) ; // x:OK
//          ok = (Ax_copy != NULL) ;
//          break ;

        default : // GrB_COO_FORMAT, nothing to allocate
            break ;
    }

    if (!ok)
    { 
        // out of memory
        GB_FREE_ALL ;
        return (GrB_OUT_OF_MEMORY) ;
    }

    //--------------------------------------------------------------------------
    // determine the # of threads to use
    //--------------------------------------------------------------------------

    GB_GET_NTHREADS_MAX (nthreads_max, chunk, Context) ;

    //--------------------------------------------------------------------------
    // copy the user input arrays
    //--------------------------------------------------------------------------

    switch (format)
    {
        case GrB_CSR_FORMAT : 
        case GrB_CSC_FORMAT : 
            GB_memcpy (Ap_copy, Ap, plen  * sizeof (GrB_Index), nthreads_max) ;
            GB_memcpy (Ai_copy, Ai, nvals * sizeof (GrB_Index), nthreads_max) ;
//      case GrB_DENSE_ROW_FORMAT :
//      case GrB_DENSE_COL_FORMAT :
            GB_memcpy (Ax_copy, Ax, nvals * typesize          , nthreads_max) ;
            break ;
        default : // GrB_COO_FORMAT, nothing to copy
            break ;
    }

    //--------------------------------------------------------------------------
    // import the matrix
    //--------------------------------------------------------------------------

    switch (format)
    {

        case GrB_CSR_FORMAT : 

            GB_OK (GB_import (false, A, type, ncols, nrows, false,
                &Ap_copy, Ap_size,  // Ap
                NULL, 0,            // Ah
                NULL, 0,            // Ab
                &Ai_copy, Ai_size,  // Ai
                (void **)
                &Ax_copy, Ax_size,  // Ax
                0, true, 0,         // CSR format may be jumbled
                GxB_SPARSE, false,  // sparse by row
                false,              // not iso
                fast_import,
                false,              // do not add to memtable
                Context)) ;
            break ;

        case GrB_CSC_FORMAT : 

            GB_OK (GB_import (false, A, type, nrows, ncols, false,
                &Ap_copy, Ap_size,  // Ap
                NULL, 0,            // Ah
                NULL, 0,            // Ab
                &Ai_copy, Ai_size,  // Ai
                (void **)
                &Ax_copy, Ax_size,  // Ax
                0, true, 0,         // CSC format may be jumbled
                GxB_SPARSE, true,   // sparse by column
                false,              // not iso
                fast_import,
                false,              // do not add to memtable
                Context)) ;
            break ;

//      case GrB_DENSE_ROW_FORMAT :
//
//          GB_OK (GB_import (false, A, type, ncols, nrows, false,
//              NULL, 0,            // Ap
//              NULL, 0,            // Ah
//              NULL, 0,            // Ab
//              NULL, 0,            // Ai
//              (void **)
//              &Ax_copy, Ax_size,  // Ax
//              0, false, 0,        // cannot be jumbled
//              GxB_FULL, false,    // full by row
//              false,              // not iso
//              fast_import,
//              false,              // do not add to memtable
//              Context)) ;
//          break ;

//      case GrB_DENSE_COL_FORMAT :
//
//          GB_OK (GB_import (false, A, type, nrows, ncols, false,
//              NULL, 0,            // Ap
//              NULL, 0,            // Ah
//              NULL, 0,            // Ab
//              NULL, 0,            // Ai
//              (void **)
//              &Ax_copy, Ax_size,  // Ax
//              0, false, 0,        // cannot be jumbled
//              GxB_FULL, true,     // full by column
//              false,              // not iso
//              fast_import,
//              false,              // do not add to memtable
//              Context)) ;
//          break ;

        default : // GrB_COO_FORMAT
            {
                // build A as hypersparse by row or by column
                int64_t *no_I_work = NULL ; size_t I_work_size = 0 ;
                int64_t *no_J_work = NULL ; size_t J_work_size = 0 ;
                GB_void *no_X_work = NULL ; size_t X_work_size = 0 ;
                bool is_csc = GB_Global_is_csc_get ( ) ;
                int64_t vlen = is_csc ? nrows : ncols ;
                int64_t vdim = is_csc ? ncols : nrows ;

                // allocate the header for A
                GB_OK (GB_new (A, // new header
                    type, vlen, vdim, GB_Ap_null, is_csc, GxB_AUTO_SPARSITY,
                    GB_Global_hyper_switch_get ( ), 0, Context)) ;

                // build A from the input triplets
                GB_OK (GB_builder (
                    *A,             // create A using a dynamic header
                    type,           // the type of A
                    vlen,
                    vdim,
                    is_csc,         // CSR/CSC format
                    &no_I_work,     // I_work_handle, not used here
                    &I_work_size,
                    &no_J_work,     // J_work_handle, not used here
                    &J_work_size,
                    &no_X_work,     // X_work_handle, not used here
                    &X_work_size,
                    false,          // known_sorted: not yet known
                    false,          // known_no_duplicates: not yet known
                    0,              // I_work, J_work, and X_work not used here
                    true,           // A is a GrB_Matrix
                    (int64_t *) (is_csc ? Ap : Ai),     // row/col indices
                    (int64_t *) (is_csc ? Ai : Ap),     // col/row indices
                    (const GB_void *) Ax,               // values
                    false,          // matrix is not iso
                    nvals,          // number of tuples
                    NULL,           // implicit SECOND operator for duplicates
                    type,           // type of the X array
                    true,           // burble is allowed
                    Context
                )) ;
            }
            break ;
    }

    ASSERT_MATRIX_OK (*A, "A imported", GB0) ;

    //--------------------------------------------------------------------------
    // determine if A is iso
    //--------------------------------------------------------------------------

    if (GB_iso_check (*A, Context))
    { 
        // All entries in A are the same; convert A to iso
        GBURBLE ("(post iso) ") ;
        (*A)->iso = true ;
        GB_OK (GB_convert_any_to_iso (*A, NULL, Context)) ;
    }

    //--------------------------------------------------------------------------
    // conform the matrix to its desired sparsity and return result
    //--------------------------------------------------------------------------

    GB_OK (GB_conform (*A, Context)) ;
    ASSERT_MATRIX_OK (*A, "final A imported", GB0) ;
    return (GrB_SUCCESS) ;
}

//------------------------------------------------------------------------------
// GrB_Matrix_import_*: import a matrix of a given type
//------------------------------------------------------------------------------

#define GB_IMPORT(prefix,ctype,T,acode)                                        \
GrB_Info GB_EVAL3 (prefix, _Matrix_import_, T) /* import a matrix */           \
(                                                                              \
    GrB_Matrix *A,          /* handle of matrix to create                    */\
    GrB_Type type,          /* type of matrix to create                      */\
    GrB_Index nrows,        /* number of rows of the matrix                  */\
    GrB_Index ncols,        /* number of columns of the matrix               */\
    const GrB_Index *Ap,    /* pointers for CSR, CSC, row indices for COO    */\
    const GrB_Index *Ai,    /* row indices for CSR, CSC, col indices for COO */\
    const ctype *Ax,        /* values (must match GrB_Type type parameter)   */\
    GrB_Index Ap_len,       /* number of entries in Ap (not # of bytes)      */\
    GrB_Index Ai_len,       /* number of entries in Ai (not # of bytes)      */\
    GrB_Index Ax_len,       /* number of entries in Ax (not # of bytes)      */\
    GrB_Format format       /* import format                                 */\
)                                                                              \
{                                                                              \
    GB_WHERE1 (GB_STR(prefix) "_Matrix_import_" GB_STR(T) " (&A, type, nrows," \
        " ncols, Ap, Ai, Ax, Ap_len, Ai_len, Ax_len, format)") ;               \
    GB_BURBLE_START (GB_STR(prefix) "_Matrix_import_" GB_STR(T)) ;             \
    GB_RETURN_IF_NULL_OR_FAULTY (type) ;                                       \
    if (type->code != acode) return (GrB_DOMAIN_MISMATCH) ;                    \
    GrB_Info info = GB_import_worker (A, type, nrows, ncols, Ap, Ai,           \
        (const void *) Ax, Ap_len, Ai_len, Ax_len, format, Context) ;          \
    GB_BURBLE_END ;                                                            \
    return (info) ;                                                            \
}

GB_IMPORT (GrB, bool      , BOOL   , GB_BOOL_code  )
GB_IMPORT (GrB, int8_t    , INT8   , GB_INT8_code  )
GB_IMPORT (GrB, int16_t   , INT16  , GB_INT16_code )
GB_IMPORT (GrB, int32_t   , INT32  , GB_INT32_code )
GB_IMPORT (GrB, int64_t   , INT64  , GB_INT64_code )
GB_IMPORT (GrB, uint8_t   , UINT8  , GB_UINT8_code )
GB_IMPORT (GrB, uint16_t  , UINT16 , GB_UINT16_code)
GB_IMPORT (GrB, uint32_t  , UINT32 , GB_UINT32_code)
GB_IMPORT (GrB, uint64_t  , UINT64 , GB_UINT64_code)
GB_IMPORT (GrB, float     , FP32   , GB_FP32_code  )
GB_IMPORT (GrB, double    , FP64   , GB_FP64_code  )
GB_IMPORT (GxB, GxB_FC32_t, FC32   , GB_FC32_code  )
GB_IMPORT (GxB, GxB_FC64_t, FC64   , GB_FC64_code  )
GB_IMPORT (GrB, void      , UDT    , GB_UDT_code   )

