//------------------------------------------------------------------------------
// UMFPACK/Source/umf_triplet: convert triplet to CSC sparse matrix
//------------------------------------------------------------------------------

// UMFPACK, Copyright (c) 2005-2023, Timothy A. Davis, All Rights Reserved.
// SPDX-License-Identifier: GPL-2.0+

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

/*
    Not user callable.  Converts triplet input to column-oriented form.
    Duplicate entries may exist (they are summed in the output).  The columns
    of the column-oriented form are in sorted order.  The input is not modified.
    Returns 1 if OK, 0 if an error occurred.

    Compiled into four different routines for each version (di, dl, zi, zl),
    for a total of 16 different routines.
*/

#include "umf_internal.h"
#include "umf_triplet.h"

#ifdef DO_MAP
#ifdef DO_VALUES
Int UMF_triplet_map_x
#else
Int UMF_triplet_map_nox
#endif
#else
#ifdef DO_VALUES
Int UMF_triplet_nomap_x
#else
Int UMF_triplet_nomap_nox
#endif
#endif
(
    Int n_row,
    Int n_col,
    Int nz,
    const Int Ti [ ],		/* size nz */
    const Int Tj [ ],		/* size nz */
    Int Ap [ ],			/* size n_col + 1 */
    Int Ai [ ],			/* size nz */
    Int Rp [ ],			/* size n_row + 1 */
    Int Rj [ ],			/* size nz */
    Int W [ ],			/* size max (n_row, n_col) */
    Int RowCount [ ]		/* size n_row */
#ifdef DO_VALUES
    , const double Tx [ ]	/* size nz */
    , double Ax [ ]		/* size nz */
    , double Rx [ ]		/* size nz */
#ifdef COMPLEX
    , const double Tz [ ]	/* size nz */
    , double Az [ ]		/* size nz */
    , double Rz [ ]		/* size nz */
#endif
#endif
#ifdef DO_MAP
    , Int Map [ ]		/* size nz */
    , Int Map2 [ ]		/* size nz */
#endif
)
{

    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int i, j, k, p, cp, p1, p2, pdest, pj ;
#ifdef DO_MAP
    Int duplicates ;
#endif
#ifdef DO_VALUES
#ifdef COMPLEX
    Int split = SPLIT (Tz) && SPLIT (Az) && SPLIT (Rz) ;
#endif
#endif

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

    /* use W as workspace for row counts (including duplicates) */
    for (i = 0 ; i < n_row ; i++)
    {
	W [i] = 0 ;
    }

    for (k = 0 ; k < nz ; k++)
    {
	i = Ti [k] ;
	j = Tj [k] ;
	if (i < 0 || i >= n_row || j < 0 || j >= n_col)
	{
	    return (UMFPACK_ERROR_invalid_matrix) ;
	}
	W [i]++ ;
#ifndef NDEBUG
	DEBUG1 ((ID " triplet: "ID" "ID" ", k, i, j)) ;
#ifdef DO_VALUES
	{
	    Entry tt ;
	    ASSIGN (tt, Tx, Tz, k, split) ;
	    EDEBUG2 (tt) ;
	    DEBUG1 (("\n")) ;
	}
#endif
#endif
    }

    /* ---------------------------------------------------------------------- */
    /* compute the row pointers */
    /* ---------------------------------------------------------------------- */

    Rp [0] = 0 ;
    for (i = 0 ; i < n_row ; i++)
    {
	Rp [i+1] = Rp [i] + W [i] ;
	W [i] = Rp [i] ;
    }

    /* W is now equal to the row pointers */

    /* ---------------------------------------------------------------------- */
    /* construct the row form */
    /* ---------------------------------------------------------------------- */

    for (k = 0 ; k < nz ; k++)
    {
	p = W [Ti [k]]++ ;
#ifdef DO_MAP
	Map [k] = p ;
#endif
	Rj [p] = Tj [k] ;
#ifdef DO_VALUES
#ifdef COMPLEX
	if (split)
	{
	    Rx [p] = Tx [k] ;
	    Rz [p] = Tz [k] ;
	}
	else
	{
	    Rx [2*p  ] = Tx [2*k  ] ;
	    Rx [2*p+1] = Tx [2*k+1] ;
	}
#else
	Rx [p] = Tx [k] ;
#endif
#endif
    }

    /* Rp stays the same, but W [i] is advanced to the start of row i+1 */

#ifndef NDEBUG
    for (i = 0 ; i < n_row ; i++)
    {
	ASSERT (W [i] == Rp [i+1]) ;
    }
#ifdef DO_MAP
    for (k = 0 ; k < nz ; k++)
    {
	/* make sure that kth triplet is mapped correctly */
	p = Map [k] ;
	DEBUG1 (("First row map: Map ["ID"] = "ID"\n", k, p)) ;
	i = Ti [k] ;
	j = Tj [k] ;
	ASSERT (j == Rj [p]) ;
	ASSERT (Rp [i] <= p && p < Rp [i+1]) ;
    }
#endif
#endif

    /* ---------------------------------------------------------------------- */
    /* sum up duplicates */
    /* ---------------------------------------------------------------------- */

    /* use W [j] to hold position in Ri/Rx/Rz of a_ij, for row i [ */

    for (j = 0 ; j < n_col ; j++)
    {
	W [j] = EMPTY ;
    }

#ifdef DO_MAP
    duplicates = FALSE ;
#endif

    for (i = 0 ; i < n_row ; i++)
    {
	p1 = Rp [i] ;
	p2 = Rp [i+1] ;
	pdest = p1 ;
	/* At this point, W [j] < p1 holds true for all columns j, */
	/* because Ri/Rx/Rz is stored in row oriented order. */
#ifndef NDEBUG
	if (UMF_debug >= -2)
	{
	    for (j = 0 ; j < n_col ; j++)
	    {
		ASSERT (W [j] < p1) ;
	    }
	}
#endif
	for (p = p1 ; p < p2 ; p++)
	{
	    j = Rj [p] ;
	    ASSERT (j >= 0 && j < n_col) ;
	    pj = W [j] ;
	    if (pj >= p1)
	    {
		/* this column index, j, is already in row i, at position pj */
		ASSERT (pj < p) ;
		ASSERT (Rj [pj] == j) ;
#ifdef DO_MAP
		Map2 [p] = pj ;
		duplicates = TRUE ;
#endif
#ifdef DO_VALUES
		/* sum the entry */
#ifdef COMPLEX
		if (split)
		{
		    Rx [pj] += Rx [p] ;
		    Rz [pj] += Rz [p] ;
		}
		else
		{
		    Rx[2*pj  ] += Rx[2*p  ] ;
		    Rx[2*pj+1] += Rx[2*p+1] ;
		}
#else
		Rx [pj] += Rx [p] ;
#endif
#endif
	    }
	    else
	    {
		/* keep the entry */
		/* also keep track in W[j] of position of a_ij for case above */
		W [j] = pdest ;
#ifdef DO_MAP
		Map2 [p] = pdest ;
#endif
		/* no need to move the entry if pdest is equal to p */
		if (pdest != p)
		{
		    Rj [pdest] = j ;
#ifdef DO_VALUES
#ifdef COMPLEX
		    if (split)
		    {
			Rx [pdest] = Rx [p] ;
			Rz [pdest] = Rz [p] ;
		    }
		    else
		    {
			Rx [2*pdest  ] = Rx [2*p  ] ;
			Rx [2*pdest+1] = Rx [2*p+1] ;
		    }
#else
		    Rx [pdest] = Rx [p] ;
#endif
#endif
		}
		pdest++ ;
	    }
	}
	RowCount [i] = pdest - p1 ;
    }

    /* done using W for position of a_ij ] */

    /* ---------------------------------------------------------------------- */
    /* merge Map and Map2 into a single Map */
    /* ---------------------------------------------------------------------- */

#ifdef DO_MAP
    if (duplicates)
    {
	for (k = 0 ; k < nz ; k++)
	{
	    Map [k] = Map2 [Map [k]] ;
	}
    }
#ifndef NDEBUG
    else
    {
	/* no duplicates, so no need to recompute Map */
	for (k = 0 ; k < nz ; k++)
	{
	    ASSERT (Map2 [k] == k) ;
	}
    }
    for (k = 0 ; k < nz ; k++)
    {
	/* make sure that kth triplet is mapped correctly */
	p = Map [k] ;
	DEBUG1 (("Second row map: Map ["ID"] = "ID"\n", k, p)) ;
	i = Ti [k] ;
	j = Tj [k] ;
	ASSERT (j == Rj [p]) ;
	ASSERT (Rp [i] <= p && p < Rp [i+1]) ;
    }
#endif
#endif

    /* now the kth triplet maps to p = Map [k], and thus to Rj/Rx [p] */

    /* ---------------------------------------------------------------------- */
    /* count the entries in each column */
    /* ---------------------------------------------------------------------- */

    /* [ use W as work space for column counts of A */
    for (j = 0 ; j < n_col ; j++)
    {
	W [j] = 0 ;
    }

    for (i = 0 ; i < n_row ; i++)
    {
	for (p = Rp [i] ; p < Rp [i] + RowCount [i] ; p++)
	{
	    j = Rj [p] ;
	    ASSERT (j >= 0 && j < n_col) ;
	    W [j]++ ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* create the column pointers */
    /* ---------------------------------------------------------------------- */

    Ap [0] = 0 ;
    for (j = 0 ; j < n_col ; j++)
    {
	Ap [j+1] = Ap [j] + W [j] ;
    }
    /* done using W as workspace for column counts of A ] */

    for (j = 0 ; j < n_col ; j++)
    {
	W [j] = Ap [j] ;
    }

    /* ---------------------------------------------------------------------- */
    /* construct the column form */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i < n_row ; i++)
    {
	for (p = Rp [i] ; p < Rp [i] + RowCount [i] ; p++)
	{
	    cp = W [Rj [p]]++ ;
#ifdef DO_MAP
	    Map2 [p] = cp ;
#endif
	    Ai [cp] = i ;
#ifdef DO_VALUES
#ifdef COMPLEX
	    if (split)
	    {
		Ax [cp] = Rx [p] ;
		Az [cp] = Rz [p] ;
	    }
	    else
	    {
		Ax [2*cp  ] = Rx [2*p  ] ;
		Ax [2*cp+1] = Rx [2*p+1] ;
	    }
#else
	    Ax [cp] = Rx [p] ;
#endif
#endif
	}
    }

    /* ---------------------------------------------------------------------- */
    /* merge Map and Map2 into a single Map */
    /* ---------------------------------------------------------------------- */

#ifdef DO_MAP
    for (k = 0 ; k < nz ; k++)
    {
	Map [k] = Map2 [Map [k]] ;
    }
#endif

    /* now the kth triplet maps to p = Map [k], and thus to Ai/Ax [p] */

#ifndef NDEBUG
    for (j = 0 ; j < n_col ; j++)
    {
	ASSERT (W [j] == Ap [j+1]) ;
    }

    UMF_dump_col_matrix (
#ifdef DO_VALUES
	Ax,
#ifdef COMPLEX
	Az,
#endif
#else
	(double *) NULL,
#ifdef COMPLEX
	(double *) NULL,
#endif
#endif
	Ai, Ap, n_row, n_col, nz) ;

#ifdef DO_MAP
    for (k = 0 ; k < nz ; k++)
    {
	/* make sure that kth triplet is mapped correctly */
	p = Map [k] ;
	DEBUG1 (("Col map: Map ["ID"] = "ID"\t", k, p)) ;
	i = Ti [k] ;
	j = Tj [k] ;
	ASSERT (i == Ai [p]) ;
	DEBUG1 (("   i "ID" j "ID" Ap[j] "ID" p "ID" Ap[j+1] "ID"\n",
		i, j, Ap [j], p, Ap [j+1])) ;
	ASSERT (Ap [j] <= p && p < Ap [j+1]) ;
    }
#endif
#endif

    return (UMFPACK_OK) ;
}
