#define LG_FREE_WORK \
{ \
LAGraph_Free ((void **) &I, NULL) ; \
LAGraph_Free ((void **) &J, NULL) ; \
LAGraph_Free ((void **) &X, NULL) ; \
}
#define LG_FREE_ALL \
{ \
LG_FREE_WORK ; \
GrB_free (A) ; \
}
#include "LG_internal.h"
static inline bool get_line
(
FILE *f, char *buf )
{
ASSERT (f != NULL) ;
ASSERT (buf != NULL) ;
buf [0] = '\0' ;
buf [1] = '\0' ;
if (fgets (buf, MAXLINE, f) == NULL)
{
return (false) ;
}
buf [MAXLINE] = '\0' ;
for (int k = 0 ; k < MAXLINE && buf [k] != '\0' ; k++)
{
buf [k] = tolower (buf [k]) ;
}
return (true) ;
}
static inline bool is_blank_line
(
char *buf )
{
ASSERT (buf != NULL) ;
if (buf [0] == '%')
{
return (true) ;
}
for (int k = 0 ; k <= MAXLINE ; k++)
{
int c = buf [k] ;
if (c == '\0')
{
break ;
}
if (!isspace (c))
{
return (false) ;
}
}
return (true) ;
}
static inline bool read_double (
char *p, double *rval )
{
while (*p && isspace (*p)) p++ ;
if (MATCH (p, "inf", 3) || MATCH (p, "+inf", 4))
{
(*rval) = INFINITY ;
}
else if (MATCH (p, "-inf", 4))
{
(*rval) = -INFINITY ;
}
else if (MATCH (p, "nan", 3))
{
(*rval) = NAN ;
}
else
{
if (sscanf (p, "%lg", rval) != 1)
{
return (false) ;
}
}
return (true) ;
}
static inline bool read_entry (
char *p, GrB_Type type, bool structural, uint8_t *x )
{
int64_t ival = 1 ;
double rval = 1, zval = 0 ;
while (*p && isspace (*p)) p++ ;
if (type == GrB_BOOL)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < 0 || ival > 1)
{
return (false) ;
}
bool *result = (bool *) x ;
result [0] = (bool) ival ;
}
else if (type == GrB_INT8)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < INT8_MIN || ival > INT8_MAX)
{
return (false) ;
}
int8_t *result = (int8_t *) x ;
result [0] = (int8_t) ival ;
}
else if (type == GrB_INT16)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < INT16_MIN || ival > INT16_MAX)
{
return (false) ;
}
int16_t *result = (int16_t *) x ;
result [0] = (int16_t) ival ;
}
else if (type == GrB_INT32)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < INT32_MIN || ival > INT32_MAX)
{
return (false) ;
}
int32_t *result = (int32_t *) x ;
result [0] = (int32_t) ival ;
}
else if (type == GrB_INT64)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
int64_t *result = (int64_t *) x ;
result [0] = (int64_t) ival ;
}
else if (type == GrB_UINT8)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < 0 || ival > UINT8_MAX)
{
return (false) ;
}
uint8_t *result = (uint8_t *) x ;
result [0] = (uint8_t) ival ;
}
else if (type == GrB_UINT16)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < 0 || ival > UINT16_MAX)
{
return (false) ;
}
uint16_t *result = (uint16_t *) x ;
result [0] = (uint16_t) ival ;
}
else if (type == GrB_UINT32)
{
if (!structural && sscanf (p, "%" SCNd64, &ival) != 1) return (false) ;
if (ival < 0 || ival > UINT32_MAX)
{
return (false) ;
}
uint32_t *result = (uint32_t *) x ;
result [0] = (uint32_t) ival ;
}
else if (type == GrB_UINT64)
{
uint64_t uval = 1 ;
if (!structural && sscanf (p, "%" SCNu64, &uval) != 1) return (false) ;
uint64_t *result = (uint64_t *) x ;
result [0] = (uint64_t) uval ;
}
else if (type == GrB_FP32)
{
if (!structural && !read_double (p, &rval)) return (false) ;
float *result = (float *) x ;
result [0] = (float) rval ;
}
else if (type == GrB_FP64)
{
if (!structural && !read_double (p, &rval)) return (false) ;
double *result = (double *) x ;
result [0] = rval ;
}
#if 0#endif
return (true) ;
}
static inline void negate_scalar
(
GrB_Type type,
uint8_t *x
)
{
if (type == GrB_INT8)
{
int8_t *value = (int8_t *) x ;
(*value) = - (*value) ;
}
else if (type == GrB_INT16)
{
int16_t *value = (int16_t *) x ;
(*value) = - (*value) ;
}
else if (type == GrB_INT32)
{
int32_t *value = (int32_t *) x ;
(*value) = - (*value) ;
}
else if (type == GrB_INT64)
{
int64_t *value = (int64_t *) x ;
(*value) = - (*value) ;
}
else if (type == GrB_FP32)
{
float *value = (float *) x ;
(*value) = - (*value) ;
}
else if (type == GrB_FP64)
{
double *value = (double *) x ;
(*value) = - (*value) ;
}
#if 0#endif
}
static inline void set_value
(
size_t typesize, GrB_Index i,
GrB_Index j,
uint8_t *x, GrB_Index *I,
GrB_Index *J,
uint8_t *X,
GrB_Index *k )
{
I [*k] = i ;
J [*k] = j ;
memcpy (X + ((*k) * typesize), x, typesize) ;
(*k)++ ;
}
int LAGraph_MMRead
(
GrB_Matrix *A, FILE *f, char *msg
)
{
GrB_Index *I = NULL, *J = NULL ;
uint8_t *X = NULL ;
LG_CLEAR_MSG ;
LG_ASSERT (A != NULL, GrB_NULL_POINTER) ;
LG_ASSERT (f != NULL, GrB_NULL_POINTER) ;
(*A) = NULL ;
MM_fmt_enum MM_fmt = MM_coordinate ;
MM_type_enum MM_type = MM_real ;
MM_storage_enum MM_storage = MM_general ;
GrB_Type type = GrB_FP64 ;
size_t typesize = sizeof (double) ;
GrB_Index nrows = 0 ;
GrB_Index ncols = 0 ;
GrB_Index nvals = 0 ;
char buf [MAXLINE+1] ;
bool got_mm_header = false ;
bool got_first_data_line = false ;
int64_t line ;
for (line = 1 ; get_line (f, buf) ; line++)
{
if ((line == 1) && MATCH (buf, "%%matrixmarket", 14))
{
got_mm_header = true ;
char *p = buf + 14 ;
while (*p && isspace (*p)) p++ ;
if (!MATCH (p, "matrix", 6))
{
LG_ASSERT_MSG (false,
LAGRAPH_IO_ERROR, "invalid MatrixMarket header"
" ('matrix' token missing)") ;
}
p += 6 ;
while (*p && isspace (*p)) p++ ;
if (MATCH (p, "coordinate", 10))
{
MM_fmt = MM_coordinate ;
p += 10 ;
}
else if (MATCH (p, "array", 5))
{
MM_fmt = MM_array ;
p += 5 ;
}
else
{
LG_ASSERT_MSG (false,
LAGRAPH_IO_ERROR, "invalid format in MatrixMarket header"
" (format must be 'coordinate' or 'array')") ;
}
while (*p && isspace (*p)) p++ ;
if (MATCH (p, "real", 4))
{
MM_type = MM_real ;
type = GrB_FP64 ;
typesize = sizeof (double) ;
p += 4 ;
}
else if (MATCH (p, "integer", 7))
{
MM_type = MM_integer ;
type = GrB_INT64 ;
typesize = sizeof (int64_t) ;
p += 7 ;
}
else if (MATCH (p, "complex", 7))
{
MM_type = MM_complex ;
#if 0#endif
LG_ASSERT_MSG (false,
GrB_NOT_IMPLEMENTED, "complex types not supported") ;
}
else if (MATCH (p, "pattern", 7))
{
MM_type = MM_pattern ;
type = GrB_BOOL ;
typesize = sizeof (bool) ;
p += 7 ;
}
else
{
LG_ASSERT_MSG (false,
LAGRAPH_IO_ERROR, "invalid MatrixMarket type") ;
}
while (*p && isspace (*p)) p++ ;
if (MATCH (p, "general", 7))
{
MM_storage = MM_general ;
}
else if (MATCH (p, "symmetric", 9))
{
MM_storage = MM_symmetric ;
}
else if (MATCH (p, "skew-symmetric", 14))
{
MM_storage = MM_skew_symmetric ;
}
else if (MATCH (p, "hermitian", 9))
{
MM_storage = MM_hermitian ;
}
else
{
LG_ASSERT_MSG (false,
LAGRAPH_IO_ERROR, "invalid MatrixMarket storage") ;
}
if (MM_type == MM_pattern)
{
LG_ASSERT_MSG (
(MM_fmt == MM_coordinate &&
(MM_storage == MM_general || MM_storage == MM_symmetric)),
LAGRAPH_IO_ERROR,
"invalid MatrixMarket pattern combination") ;
}
if (MM_storage == MM_hermitian)
{
LG_ASSERT_MSG (MM_type == MM_complex,
LAGRAPH_IO_ERROR,
"invalid MatrixMarket complex combination") ;
}
}
else if (got_mm_header && MATCH (buf, "%%graphblas", 11))
{
char *p = buf + 11 ;
while (*p && isspace (*p)) p++ ;
if (MATCH (p, "type", 4) && !got_first_data_line)
{
p += 4 ;
while (*p && isspace (*p)) p++ ;
if (MATCH (p, "bool", 4))
{
type = GrB_BOOL ;
typesize = sizeof (bool) ;
}
else if (MATCH (p, "int8_t", 6))
{
type = GrB_INT8 ;
typesize = sizeof (int8_t) ;
}
else if (MATCH (p, "int16_t", 7))
{
type = GrB_INT16 ;
typesize = sizeof (int16_t) ;
}
else if (MATCH (p, "int32_t", 7))
{
type = GrB_INT32 ;
typesize = sizeof (int32_t) ;
}
else if (MATCH (p, "int64_t", 7))
{
type = GrB_INT64 ;
typesize = sizeof (int64_t) ;
}
else if (MATCH (p, "uint8_t", 7))
{
type = GrB_UINT8 ;
typesize = sizeof (uint8_t) ;
}
else if (MATCH (p, "uint16_t", 8))
{
type = GrB_UINT16 ;
typesize = sizeof (uint16_t) ;
}
else if (MATCH (p, "uint32_t", 8))
{
type = GrB_UINT32 ;
typesize = sizeof (uint32_t) ;
}
else if (MATCH (p, "uint64_t", 8))
{
type = GrB_UINT64 ;
typesize = sizeof (uint64_t) ;
}
else if (MATCH (p, "float complex", 13))
{
#if 0#endif
LG_ASSERT_MSG (false,
GrB_NOT_IMPLEMENTED, "complex types not supported") ;
}
else if (MATCH (p, "double complex", 14))
{
#if 0#endif
LG_ASSERT_MSG (false,
GrB_NOT_IMPLEMENTED, "complex types not supported") ;
}
else if (MATCH (p, "float", 5))
{
type = GrB_FP32 ;
typesize = sizeof (float) ;
}
else if (MATCH (p, "double", 6))
{
type = GrB_FP64 ;
typesize = sizeof (double) ;
}
else
{
LG_ASSERT_MSG (false,
LAGRAPH_IO_ERROR, "unknown type") ;
}
if (MM_storage == MM_skew_symmetric && (type == GrB_BOOL ||
type == GrB_UINT8 || type == GrB_UINT16 ||
type == GrB_UINT32 || type == GrB_UINT64))
{
LG_ASSERT_MSG (false, LAGRAPH_IO_ERROR,
"skew-symmetric matrices cannot have an unsigned type");
}
}
else
{
continue ;
}
}
else if (is_blank_line (buf))
{
continue ;
}
else
{
got_first_data_line = true ;
int nitems = sscanf (buf, "%" SCNu64 " %" SCNu64 " %" SCNu64,
&nrows, &ncols, &nvals) ;
if (nitems == 2)
{
if (!got_mm_header)
{
MM_fmt = MM_array ;
MM_type = MM_real ;
MM_storage = MM_general ;
type = GrB_FP64 ;
typesize = sizeof (double) ;
}
if (MM_storage == MM_general)
{
nvals = nrows * ncols ;
}
else
{
nvals = nrows + ((nrows * nrows - nrows) / 2) ;
}
}
else if (nitems == 3)
{
if (!got_mm_header)
{
MM_fmt = MM_coordinate ;
MM_type = MM_real ;
MM_storage = MM_general ;
type = GrB_FP64 ;
typesize = sizeof (double) ;
}
}
else
{
LG_ASSERT_MSGF (false,
LAGRAPH_IO_ERROR, "invalid 1st data line"
" (line %" PRId64 " of input file)", line) ;
}
if (nrows != ncols)
{
LG_ASSERT_MSG (MM_storage == MM_general,
LAGRAPH_IO_ERROR, "invalid rectangular storage") ;
}
break ;
}
}
GRB_TRY (GrB_Matrix_new (A, type, nrows, ncols)) ;
if (nrows == 0 || ncols == 0 || nvals == 0)
{
return (GrB_SUCCESS) ;
}
GrB_Index nvals3 = ((MM_storage == MM_general) ? 1 : 2) * (nvals + 1) ;
LG_TRY (LAGraph_Malloc ((void **) &I, nvals3, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &J, nvals3, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &X, nvals3, typesize, msg)) ;
GrB_Index i = -1, j = 0 ;
GrB_Index nvals2 = 0 ;
for (int64_t k = 0 ; k < nvals ; k++)
{
uint8_t x [MAXLINE] ;
while (true)
{
bool ok = get_line (f, buf) ;
line++ ;
LG_ASSERT_MSG (ok, LAGRAPH_IO_ERROR, "premature EOF") ;
if (is_blank_line (buf))
{
continue ;
}
char *p = buf ;
if (MM_fmt == MM_array)
{
i++ ;
if (i == nrows)
{
j++ ;
if (MM_storage == MM_general)
{
i = 0 ;
}
else
{
i = j ;
}
}
}
else
{
int inputs = sscanf (p, "%" SCNu64 " %" SCNu64, &i, &j) ;
LG_ASSERT_MSGF (inputs == 2, LAGRAPH_IO_ERROR,
"line %" PRId64 " of input file: indices invalid", line) ;
LG_ASSERT_MSGF (i >= 1 && i <= nrows, GrB_INDEX_OUT_OF_BOUNDS,
"line %" PRId64 " of input file: row index %" PRIu64
" out of range (must be in range 1 to %" PRIu64")",
line, i, nrows) ;
LG_ASSERT_MSGF (j >= 1 && j <= ncols, GrB_INDEX_OUT_OF_BOUNDS,
"line %" PRId64 " of input file: column index %" PRIu64
" out of range (must be in range 1 to %" PRIu64")",
line, j, ncols) ;
i-- ;
j-- ;
while (*p && isspace (*p)) p++ ; while (*p && !isspace (*p)) p++ ; while (*p && isspace (*p)) p++ ; while (*p && !isspace (*p)) p++ ; }
while (*p && isspace (*p)) p++ ;
ok = read_entry (p, type, MM_type == MM_pattern, x) ;
LG_ASSERT_MSGF (ok, LAGRAPH_IO_ERROR, "entry value invalid on line"
" %" PRId64 " of input file", line) ;
set_value (typesize, i, j, x, I, J, X, &nvals2) ;
if (i != j && MM_storage != MM_general)
{
if (MM_storage == MM_symmetric)
{
set_value (typesize, j, i, x, I, J, X, &nvals2) ;
}
else if (MM_storage == MM_skew_symmetric)
{
negate_scalar (type, x) ;
set_value (typesize, j, i, x, I, J, X, &nvals2) ;
}
#if 0 #endif
}
break ;
}
}
if (type == GrB_BOOL)
{
GRB_TRY (GrB_Matrix_build_BOOL (*A, I, J, (bool *) X, nvals2, GxB_IGNORE_DUP)) ;
}
else if (type == GrB_INT8)
{
GRB_TRY (GrB_Matrix_build_INT8 (*A, I, J, (int8_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_INT16)
{
GRB_TRY (GrB_Matrix_build_INT16 (*A, I, J, (int16_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_INT32)
{
GRB_TRY (GrB_Matrix_build_INT32 (*A, I, J, (int32_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_INT64)
{
GRB_TRY (GrB_Matrix_build_INT64 (*A, I, J, (int64_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_UINT8)
{
GRB_TRY (GrB_Matrix_build_UINT8 (*A, I, J, (uint8_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_UINT16)
{
GRB_TRY (GrB_Matrix_build_UINT16 (*A, I, J, (uint16_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_UINT32)
{
GRB_TRY (GrB_Matrix_build_UINT32 (*A, I, J, (uint32_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_UINT64)
{
GRB_TRY (GrB_Matrix_build_UINT64 (*A, I, J, (uint64_t *) X, nvals2, NULL)) ;
}
else if (type == GrB_FP32)
{
GRB_TRY (GrB_Matrix_build_FP32 (*A, I, J, (float *) X, nvals2, NULL)) ;
}
else if (type == GrB_FP64)
{
GRB_TRY (GrB_Matrix_build_FP64 (*A, I, J, (double *) X, nvals2, NULL)) ;
}
#if 0#endif
#if LAGRAPH_SUITESPARSE
#if GxB_IMPLEMENTATION >= GxB_VERSION (10,0,0)
GRB_TRY (GrB_Matrix_set_INT32 (*A, 32, GxB_ROWINDEX_INTEGER_HINT)) ;
GRB_TRY (GrB_Matrix_set_INT32 (*A, 32, GxB_COLINDEX_INTEGER_HINT)) ;
GRB_TRY (GrB_Matrix_set_INT32 (*A, 32, GxB_OFFSET_INTEGER_HINT)) ;
#endif
#endif
LG_FREE_WORK ;
return (GrB_SUCCESS) ;
}