//------------------------------------------------------------------------------
// CHOLMOD/Core/cholmod_triplet: core methods for the cholmod_triplet object
//------------------------------------------------------------------------------

// CHOLMOD/Core Module.  Copyright (C) 2005-2022, University of Florida.
// All Rights Reserved. Author:  Timothy A. Davis
// SPDX-License-Identifier: LGPL-2.1+

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

/* Core utility routines for the cholmod_triplet object:
 *
 * A sparse matrix held in triplet form is the simplest one for a user to
 * create.  It consists of a list of nz entries in arbitrary order, held in
 * three arrays: i, j, and x, each of length nk.  The kth entry is in row i[k],
 * column j[k], with value x[k].  There may be duplicate values; if A(i,j)
 * appears more than once, its value is the sum of the entries with those row
 * and column indices.
 *
 * Primary routines:
 * -----------------
 * cholmod_allocate_triplet	allocate a triplet matrix
 * cholmod_free_triplet		free a triplet matrix
 *
 * Secondary routines:
 * -------------------
 * cholmod_reallocate_triplet	reallocate a triplet matrix
 * cholmod_sparse_to_triplet	create a triplet matrix copy of a sparse matrix
 * cholmod_triplet_to_sparse	create a sparse matrix copy of a triplet matrix
 * cholmod_copy_triplet		create a copy of a triplet matrix
 *
 * The relationship between an m-by-n cholmod_sparse matrix A and a
 * cholmod_triplet matrix (i, j, and x) is identical to how they are used in
 * the MATLAB "sparse" and "find" functions:
 *
 *	[i j x] = find (A)
 *	[m n] = size (A)
 *	A = sparse (i,j,x,m,n)
 *
 * with the exception that the cholmod_sparse matrix may be "unpacked", may
 * have either sorted or unsorted columns (depending on the option selected),
 * and may be symmetric with just the upper or lower triangular part stored.
 * Likewise, the cholmod_triplet matrix may contain just the entries in the
 * upper or lower triangular part of a symmetric matrix.
 *
 * MATLAB sparse matrices are always "packed", always have sorted columns,
 * and always store both parts of a symmetric matrix.  In some cases, MATLAB
 * behaves like CHOLMOD by ignoring entries in the upper or lower triangular
 * part of a matrix that is otherwise assumed to be symmetric (such as the
 * input to chol).  In CHOLMOD, that option is a characteristic of the object.
 * In MATLAB, that option is based on how a matrix is used as the input to
 * a function.
 *
 * The triplet matrix is provided to give the user a simple way of constructing
 * a sparse matrix.  There are very few operations supported for triplet
 * matrices.  The assumption is that they will be converted to cholmod_sparse
 * matrix form first.
 *
 * Adding two triplet matrices simply involves concatenating the contents of
 * the three arrays (i, j, and x).   To permute a triplet matrix, just replace
 * the row and column indices with their permuted values.  For example, if
 * P is a permutation vector, then P [k] = j means row/column j is the kth
 * row/column in C=P*A*P'.  In MATLAB notation, C=A(p,p).  If Pinv is an array
 * of size n and T is the triplet form of A, then:
 *
 *	Ti = T->i ;
 *	Tj = T->j ;
 *	for (k = 0 ; k < n  ; k++) Pinv [P [k]] = k ;
 *	for (k = 0 ; k < nz ; k++) Ti [k] = Pinv [Ti [k]] ;
 *	for (k = 0 ; k < nz ; k++) Tj [k] = Pinv [Tj [k]] ;
 *
 * overwrites T with the triplet form of C=P*A*P'.  The conversion
 *
 *	C = cholmod_triplet_to_sparse (T, 0, &Common) ;
 *
 * will then return the matrix C = P*A*P'.
 *
 * Note that T->stype > 0 means that entries in the lower triangular part of
 * T are transposed into the upper triangular part when T is converted to
 * sparse matrix (cholmod_sparse) form with cholmod_triplet_to_sparse.  The
 * opposite is true for T->stype < 0.
 *
 * Since the triplet matrix T is so simple to generate, it's quite easy
 * to remove entries that you do not want, prior to converting T to the
 * cholmod_sparse form.  So if you include these entries in T, CHOLMOD
 * assumes that there must be a reason (such as the one above).  Thus,
 * no entry in a triplet matrix is ever ignored.
 *
 * Other operations, such as extacting a submatrix, horizontal and vertical
 * concatenation, multiply a triplet matrix times a dense matrix, are also
 * simple.  Multiplying two triplet matrices is not trivial; the simplest
 * method is to convert them to cholmod_sparse matrices first.
 *
 * Supports all xtypes (pattern, real, complex, and zomplex).
 */
 
#include "cholmod_internal.h"

/* ========================================================================== */
/* === TEMPLATE ============================================================= */
/* ========================================================================== */

#define PATTERN
#include "t_cholmod_triplet.c"
#define REAL
#include "t_cholmod_triplet.c"
#define COMPLEX
#include "t_cholmod_triplet.c"
#define ZOMPLEX
#include "t_cholmod_triplet.c"

/* ========================================================================== */
/* === cholmod_allocate_triplet ============================================= */
/* ========================================================================== */

/* allocate space for a triplet matrix
 *
 * workspace: none
 */

cholmod_triplet *CHOLMOD(allocate_triplet)
(
    /* ---- input ---- */
    size_t nrow,	/* # of rows of T */
    size_t ncol,	/* # of columns of T */
    size_t nzmax,	/* max # of nonzeros of T */
    int stype,		/* stype of T */
    int xtype,		/* CHOLMOD_PATTERN, _REAL, _COMPLEX, or _ZOMPLEX */
    /* --------------- */
    cholmod_common *Common
)
{
    cholmod_triplet *T ;
    size_t nzmax0 ;
    int ok = TRUE ;

    /* ---------------------------------------------------------------------- */
    /* get inputs */
    /* ---------------------------------------------------------------------- */

    RETURN_IF_NULL_COMMON (NULL) ;
    if (xtype < CHOLMOD_PATTERN || xtype > CHOLMOD_ZOMPLEX)
    {
	ERROR (CHOLMOD_INVALID, "xtype invalid") ;
	return (NULL) ;
    }
    /* ensure the dimensions do not cause integer overflow */
    (void) CHOLMOD(add_size_t) (ncol, 2, &ok) ;
    if (!ok || (int64_t) nrow  >= Int_max
            || (int64_t) ncol  >= Int_max
            || (int64_t) nzmax >= Int_max)
    {
	ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
	return (NULL) ;
    }

    Common->status = CHOLMOD_OK ;

    /* ---------------------------------------------------------------------- */
    /* allocate header */
    /* ---------------------------------------------------------------------- */

    T = CHOLMOD(malloc) (sizeof (cholmod_triplet), 1, Common) ;
    if (Common->status < CHOLMOD_OK)
    {
	return (NULL) ;	    /* out of memory */
    }

    PRINT1 (("cholmod_allocate_triplet %d-by-%d nzmax %d xtype %d\n",
		nrow, ncol, nzmax, xtype)) ;

    nzmax = MAX (1, nzmax) ;

    T->nrow = nrow ;
    T->ncol = ncol ;
    T->nzmax = nzmax ;
    T->nnz = 0 ;
    T->stype = stype ;
    T->itype = ITYPE ;
    T->xtype = xtype ;
    T->dtype = DTYPE ;

    T->j = NULL ;
    T->i = NULL ;
    T->x = NULL ;
    T->z = NULL ;

    /* ---------------------------------------------------------------------- */
    /* allocate the matrix itself */
    /* ---------------------------------------------------------------------- */

    nzmax0 = 0 ;
    CHOLMOD(realloc_multiple) (nzmax, 2, xtype, &(T->i), &(T->j),
		&(T->x), &(T->z), &nzmax0, Common) ;

    if (Common->status < CHOLMOD_OK)
    {
	CHOLMOD(free_triplet) (&T, Common) ;
	return (NULL) ;	    /* out of memory */
    }

    return (T) ;
}


/* ========================================================================== */
/* === cholmod_free_triplet ================================================= */
/* ========================================================================== */

/* free a triplet matrix
 *
 * workspace: none
 */

int CHOLMOD(free_triplet)
(
    /* ---- in/out --- */
    cholmod_triplet **THandle,    /* matrix to deallocate, NULL on output */
    /* --------------- */
    cholmod_common *Common
)
{
    Int nz ;
    cholmod_triplet *T ;

    RETURN_IF_NULL_COMMON (FALSE) ;

    if (THandle == NULL)
    {
	/* nothing to do */
	return (TRUE) ;
    }
    T = *THandle ;
    if (T == NULL)
    {
	/* nothing to do */
	return (TRUE) ;
    }
    nz = T->nzmax ;
    T->j = CHOLMOD(free) (nz, sizeof (Int), T->j, Common) ;
    T->i = CHOLMOD(free) (nz, sizeof (Int), T->i, Common) ;
    if (T->xtype == CHOLMOD_REAL)
    {
	T->x = CHOLMOD(free) (nz, sizeof (double), T->x, Common) ;
    }
    else if (T->xtype == CHOLMOD_COMPLEX)
    {
	T->x = CHOLMOD(free) (nz, 2*sizeof (double), T->x, Common) ;
    }
    else if (T->xtype == CHOLMOD_ZOMPLEX)
    {
	T->x = CHOLMOD(free) (nz, sizeof (double), T->x, Common) ;
	T->z = CHOLMOD(free) (nz, sizeof (double), T->z, Common) ;
    }
    *THandle = CHOLMOD(free) (1, sizeof (cholmod_triplet), (*THandle), Common) ;
    return (TRUE) ;
}


/* ========================================================================== */
/* === cholmod_reallocate_triplet =========================================== */
/* ========================================================================== */

/* Change the size of T->i, T->j, and T->x, or allocate them if their current
 * size is zero.  T->x is not modified if T->xtype is CHOLMOD_PATTERN.
 *
 * workspace: none
 */

int CHOLMOD(reallocate_triplet)
(
    /* ---- input ---- */
    size_t nznew,	/* new # of entries in T */
    /* ---- in/out --- */
    cholmod_triplet *T,	/* triplet matrix to modify */
    /* --------------- */
    cholmod_common *Common
)
{

    /* ---------------------------------------------------------------------- */
    /* get inputs */
    /* ---------------------------------------------------------------------- */

    RETURN_IF_NULL_COMMON (FALSE) ;
    RETURN_IF_NULL (T, FALSE) ;
    RETURN_IF_XTYPE_INVALID (T, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ;
    Common->status = CHOLMOD_OK ;
    PRINT1 (("realloc triplet %d to %d, xtype: %d\n",
		T->nzmax, nznew, T->xtype)) ;

    /* ---------------------------------------------------------------------- */
    /* resize the matrix */
    /* ---------------------------------------------------------------------- */

    CHOLMOD(realloc_multiple) (MAX (1,nznew), 2, T->xtype, &(T->i), &(T->j),
	    &(T->x), &(T->z), &(T->nzmax), Common) ;

    return (Common->status == CHOLMOD_OK) ;
}


/* ========================================================================== */
/* === cholmod_triplet_to_sparse ============================================ */
/* ========================================================================== */

/* Convert a set of triplets into a cholmod_sparse matrix.  In MATLAB notation,
 * for unsymmetric matrices:
 *
 *	A = sparse (Ti, Tj, Tx, nrow, ncol, nzmax) ;
 *
 * For the symmetric upper case:
 *
 *	A = sparse (min(Ti,Tj), max(Ti,Tj), Tx, nrow, ncol, nzmax) ;
 *
 * For the symmetric lower case:
 *
 *	A = sparse (max(Ti,Tj), min(Ti,Tj), Tx, nrow, ncol, nzmax) ;
 *
 * If Tx is NULL, then A->x is not allocated, and only the pattern of A is
 * computed.  A is returned in packed form, and can be of any stype
 * (upper/lower/unsymmetric).  It has enough space to hold the values in T,
 * or nzmax, whichever is larger.
 *
 * workspace: Iwork (max (nrow,ncol))
 *	allocates a temporary copy of its output matrix.
 *
 * The resulting sparse matrix has the same xtype as the input triplet matrix.
 */

cholmod_sparse *CHOLMOD(triplet_to_sparse)
(
    /* ---- input ---- */
    cholmod_triplet *T,	/* matrix to copy */
    size_t nzmax,	/* allocate at least this much space in output matrix */
    /* --------------- */
    cholmod_common *Common
)
{
    cholmod_sparse *R, *A = NULL ;
    Int *Wj, *Rp, *Ri, *Rnz, *Ti, *Tj ;
    Int i, j, p, k, stype, nrow, ncol, nz, ok ;
    size_t anz = 0 ;

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

    RETURN_IF_NULL_COMMON (NULL) ;
    RETURN_IF_NULL (T, NULL) ;
    Ti = T->i ;
    Tj = T->j ;
    RETURN_IF_NULL (Ti, NULL) ;
    RETURN_IF_NULL (Tj, NULL) ;
    RETURN_IF_XTYPE_INVALID (T, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, NULL) ;
    stype = SIGN (T->stype) ;
    if (stype && T->nrow != T->ncol)
    {
	/* inputs invalid */
	ERROR (CHOLMOD_INVALID, "matrix invalid") ;
	return (NULL) ;
    }
    Common->status = CHOLMOD_OK ;
    DEBUG (CHOLMOD(dump_triplet) (T, "T", Common)) ;

    /* ---------------------------------------------------------------------- */
    /* get inputs */
    /* ---------------------------------------------------------------------- */

    nrow = T->nrow ;
    ncol = T->ncol ;
    nz = T->nnz ;

    /* ---------------------------------------------------------------------- */
    /* allocate workspace */
    /* ---------------------------------------------------------------------- */

    CHOLMOD(allocate_work) (0, MAX (nrow, ncol), 0, Common) ;
    if (Common->status < CHOLMOD_OK)
    {
	return (NULL) ;	    /* out of memory */
    }

    /* ---------------------------------------------------------------------- */
    /* allocate temporary matrix R */
    /* ---------------------------------------------------------------------- */

    R = CHOLMOD(allocate_sparse) (ncol, nrow, nz, FALSE, FALSE, -stype,
	    T->xtype, Common) ;

    if (Common->status < CHOLMOD_OK)
    {
	return (NULL) ;	    /* out of memory */
    }

    Rp = R->p ;
    Ri = R->i ;
    Rnz = R->nz ;

    /* ---------------------------------------------------------------------- */
    /* count the entries in each row of A (also counting duplicates) */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i < nrow ; i++)
    {
	Rnz [i] = 0 ;	
    }

    if (stype > 0)
    {
	for (k = 0 ; k < nz ; k++)
	{
	    i = Ti [k] ;
	    j = Tj [k] ;
	    if (i < 0 || i >= nrow || j < 0 || j >= ncol)
	    {
		ERROR (CHOLMOD_INVALID, "index out of range") ;
		break ;
	    }
	    /* A will be symmetric with just the upper triangular part stored.
	     * Create a matrix R that is lower triangular.  Entries in the
	     * upper part of R are transposed to the lower part. */
	    Rnz [MIN (i,j)]++ ;
	}
    }
    else if (stype < 0)
    {
	for (k = 0 ; k < nz ; k++)
	{
	    i = Ti [k] ;
	    j = Tj [k] ;
	    if (i < 0 || i >= nrow || j < 0 || j >= ncol)
	    {
		ERROR (CHOLMOD_INVALID, "index out of range") ;
		break ;
	    }
	    /* A will be symmetric with just the lower triangular part stored.
	     * Create a matrix R that is upper triangular.  Entries in the
	     * lower part of R are transposed to the upper part. */
	    Rnz [MAX (i,j)]++ ;
	}
    }
    else
    {
	for (k = 0 ; k < nz ; k++)
	{
	    i = Ti [k] ;
	    j = Tj [k] ;
	    if (i < 0 || i >= nrow || j < 0 || j >= ncol)
	    {
		ERROR (CHOLMOD_INVALID, "index out of range") ;
		break ;
	    }
	    /* constructing an unsymmetric matrix */
	    Rnz [i]++ ;
	}
    }

    if (Common->status < CHOLMOD_OK)
    {
	/* triplet matrix is invalid */
	CHOLMOD(free_sparse) (&R, Common) ;
	return (NULL) ;
    }

    /* ---------------------------------------------------------------------- */
    /* construct the row pointers */
    /* ---------------------------------------------------------------------- */

    p = 0 ;
    for (i = 0 ; i < nrow ; i++)
    {
	Rp [i] = p ;
	p += Rnz [i] ;
    }
    Rp [nrow] = p ;

    /* use Wj (i/l/l) as temporary row pointers */
    Wj = Common->Iwork ;	/* size MAX (nrow,ncol) FUTURE WORK: (i/l/l) */
    for (i = 0 ; i < nrow ; i++)
    {
	Wj [i] = Rp [i] ;
    }

    /* ---------------------------------------------------------------------- */
    /* construct triplet matrix, using template routine */
    /* ---------------------------------------------------------------------- */

    switch (T->xtype)
    {
	case CHOLMOD_PATTERN:
	    anz = p_cholmod_triplet_to_sparse (T, R, Common) ;
	    break ;

	case CHOLMOD_REAL:
	    anz = r_cholmod_triplet_to_sparse (T, R, Common) ;
	    break ;

	case CHOLMOD_COMPLEX:
	    anz = c_cholmod_triplet_to_sparse (T, R, Common) ;
	    break ;

	case CHOLMOD_ZOMPLEX:
	    anz = z_cholmod_triplet_to_sparse (T, R, Common) ;
	    break ;
    }

    /* ---------------------------------------------------------------------- */
    /* A = R' (array transpose, not complex conjugate transpose) */
    /* ---------------------------------------------------------------------- */

    /* workspace: Iwork (R->nrow), which is A->ncol */

    ASSERT (CHOLMOD(dump_sparse) (R, "R", Common) >= 0) ;

    A = CHOLMOD(allocate_sparse) (nrow, ncol, MAX (anz, nzmax), TRUE, TRUE,
	stype, T->xtype, Common) ;

    if (stype)
    {
	ok = CHOLMOD(transpose_sym) (R, 1, NULL, A, Common) ;
    }
    else
    {
	ok = CHOLMOD(transpose_unsym) (R, 1, NULL, NULL, 0, A, Common) ; 
    }

    CHOLMOD(free_sparse) (&R, Common) ;
    if (Common->status < CHOLMOD_OK)
    {
	CHOLMOD(free_sparse) (&A, Common) ;
    }

    /* ---------------------------------------------------------------------- */
    /* return result */
    /* ---------------------------------------------------------------------- */

    ASSERT (CHOLMOD(dump_sparse) (A, "A = triplet(T) result", Common) >= 0) ;
    return (A) ;
}


/* ========================================================================== */
/* === cholmod_sparse_to_triplet ============================================ */
/* ========================================================================== */

/* Converts a sparse column-oriented matrix to triplet form.
 * The resulting triplet matrix has the same xtype as the sparse matrix.
 *
 * workspace: none
 */

cholmod_triplet *CHOLMOD(sparse_to_triplet)
(
    /* ---- input ---- */
    cholmod_sparse *A,	/* matrix to copy */
    /* --------------- */
    cholmod_common *Common
)
{
    double *Ax, *Az, *Tx, *Tz ;
    Int *Ap, *Ai, *Ti, *Tj, *Anz ;
    cholmod_triplet *T ;
    Int i, xtype, p, pend, k, j, nrow, ncol, nz, stype, packed, up, lo,
	both ;

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

    RETURN_IF_NULL_COMMON (NULL) ;
    RETURN_IF_NULL (A, NULL) ;
    RETURN_IF_XTYPE_INVALID (A, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, NULL) ;
    stype = SIGN (A->stype) ;
    nrow = A->nrow ;
    ncol = A->ncol ;
    if (stype && nrow != ncol)
    {
	/* inputs invalid */
	ERROR (CHOLMOD_INVALID, "matrix invalid") ;
	return (NULL) ;
    }
    Ax = A->x ;
    Az = A->z ;
    xtype = A->xtype ;
    Common->status = CHOLMOD_OK ;

    ASSERT (CHOLMOD(dump_sparse) (A, "A", Common) >= 0) ;

    /* ---------------------------------------------------------------------- */
    /* allocate triplet matrix */
    /* ---------------------------------------------------------------------- */

    nz = CHOLMOD(nnz) (A, Common) ;
    T = CHOLMOD(allocate_triplet) (nrow, ncol, nz, A->stype, A->xtype, Common) ;

    if (Common->status < CHOLMOD_OK)
    {
	return (NULL) ;	    /* out of memory */
    }

    /* ---------------------------------------------------------------------- */
    /* convert to a sparse matrix */
    /* ---------------------------------------------------------------------- */

    Ap = A->p ;
    Ai = A->i ;
    Anz = A->nz ;
    packed = A->packed ;

    Ti = T->i ;
    Tj = T->j ;
    Tx = T->x ;
    Tz = T->z ;
    T->stype = A->stype ;

    both = (A->stype == 0) ;
    up = (A->stype > 0) ;
    lo = (A->stype < 0) ;

    k = 0 ;

    for (j = 0 ; j < ncol ; j++)
    {
	p = Ap [j] ;
	pend = (packed) ? (Ap [j+1]) : (p + Anz [j]) ;
	for ( ; p < pend ; p++)
	{
	    i = Ai [p] ;
	    if (both || (up && i <= j) || (lo && i >= j))
	    {
		Ti [k] = Ai [p] ;
		Tj [k] = j ;

		if (xtype == CHOLMOD_REAL)
		{
		    Tx [k] = Ax [p] ;
		}
		else if (xtype == CHOLMOD_COMPLEX)
		{
		    Tx [2*k  ] = Ax [2*p  ] ;
		    Tx [2*k+1] = Ax [2*p+1] ;
		}
		else if (xtype == CHOLMOD_ZOMPLEX)
		{
		    Tx [k] = Ax [p] ;
		    Tz [k] = Az [p] ;
		}

		k++ ;
		ASSERT (k <= nz) ;
	    }
	}
    }

    T->nnz = k ;

    /* ---------------------------------------------------------------------- */
    /* return result */
    /* ---------------------------------------------------------------------- */

    ASSERT (CHOLMOD(dump_triplet) (T, "T", Common)) ;
    return (T) ;
}


/* ========================================================================== */
/* === cholmod_copy_triplet ================================================= */
/* ========================================================================== */

/* Create an exact copy of a triplet matrix, except that entries in unused
 * space are not copied (they might not be initialized, and copying them would
 * cause program checkers such as purify and valgrind to complain).
 * The output triplet matrix has the same xtype as the input triplet matrix.
 */

cholmod_triplet *CHOLMOD(copy_triplet)
(
    /* ---- input ---- */
    cholmod_triplet *T,	/* matrix to copy */
    /* --------------- */
    cholmod_common *Common
)
{
    double *Tx, *Tz, *Cx, *Cz ;
    Int *Ci, *Cj, *Ti, *Tj ;
    cholmod_triplet *C ;
    Int xtype, k, nz ;

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

    RETURN_IF_NULL_COMMON (NULL) ;
    RETURN_IF_NULL (T, NULL) ;
    RETURN_IF_XTYPE_INVALID (T, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, NULL) ;
    nz = T->nnz ;
    Ti = T->i ;
    Tj = T->j ;
    Tx = T->x ;
    Tz = T->z ;
    xtype = T->xtype ;
    RETURN_IF_NULL (Ti, NULL) ;
    RETURN_IF_NULL (Tj, NULL) ;
    Common->status = CHOLMOD_OK ;
    DEBUG (CHOLMOD(dump_triplet) (T, "T input", Common)) ;

    /* ---------------------------------------------------------------------- */
    /* allocate copy */
    /* ---------------------------------------------------------------------- */

    C = CHOLMOD(allocate_triplet) (T->nrow, T->ncol, T->nzmax, T->stype,
	    xtype, Common) ;

    if (Common->status < CHOLMOD_OK)
    {
	return (NULL) ;	    /* out of memory */
    }

    /* ---------------------------------------------------------------------- */
    /* copy the triplet matrix */
    /* ---------------------------------------------------------------------- */

    Ci = C->i ;
    Cj = C->j ;
    Cx = C->x ;
    Cz = C->z ;
    C->nnz = nz ;

    for (k = 0 ; k < nz ; k++)
    {
	Ci [k] = Ti [k] ;
    }
    for (k = 0 ; k < nz ; k++)
    {
	Cj [k] = Tj [k] ;
    }

    if (xtype == CHOLMOD_REAL)
    {
	for (k = 0 ; k < nz ; k++)
	{
	    Cx [k] = Tx [k] ;
	}
    }
    else if (xtype == CHOLMOD_COMPLEX)
    {
	for (k = 0 ; k < nz ; k++)
	{
	    Cx [2*k  ] = Tx [2*k  ] ;
	    Cx [2*k+1] = Tx [2*k+1] ;
	}
    }
    else if (xtype == CHOLMOD_ZOMPLEX)
    {
	for (k = 0 ; k < nz ; k++)
	{
	    Cx [k] = Tx [k] ;
	    Cz [k] = Tz [k] ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* return the result */
    /* ---------------------------------------------------------------------- */

    ASSERT (CHOLMOD(dump_triplet) (C, "C triplet copy", Common)) ;
    return (C) ;
}
