//------------------------------------------------------------------------------
// UMFPACK/Source/umf_transpose: transpose/permute a sparse matrix
//------------------------------------------------------------------------------

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

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

/*  Not user-callable.  Computes a permuted transpose, R = (A (P,Q(1:nq)))' in
	MATLAB notation, where R is in column-form.  A is n_row-by-n_col, the
	row-form matrix R is n_row-by-nq, where nq <= n_col.  A may be singular.
	The complex version can do transpose (') or array transpose (.').

	Uses Gustavson's method (Two Fast Algorithms for Sparse Matrices:
	Multiplication and Permuted Transposition, ACM Trans. on Math. Softw.,
	vol 4, no 3, pp. 250-269).
*/

#include "umf_internal.h"
#include "umf_transpose.h"
#include "umf_is_permutation.h"

Int UMF_transpose
(
    Int n_row,			/* A is n_row-by-n_col */
    Int n_col,
    const Int Ap [ ],		/* size n_col+1 */
    const Int Ai [ ],		/* size nz = Ap [n_col] */
    const double Ax [ ],	/* size nz if present */

    const Int P [ ],	/* P [k] = i means original row i is kth row in A(P,Q)*/
			/* P is identity if not present */
			/* size n_row, if present */

    const Int Q [ ],	/* Q [k] = j means original col j is kth col in A(P,Q)*/
			/* Q is identity if not present */
			/* size nq, if present */
    Int nq,		/* size of Q, ignored if Q is (Int *) NULL */

			/* output matrix: Rp, Ri, Rx, and Rz: */
    Int Rp [ ],		/* size n_row+1 */
    Int Ri [ ],		/* size nz */
    double Rx [ ],	/* size nz, if present */

    Int W [ ],		/* size max (n_row,n_col) workspace */

    Int check		/* if true, then check inputs */
#ifdef COMPLEX
    , const double Az [ ]	/* size nz */
    , double Rz [ ]		/* size nz */
    , Int do_conjugate		/* if true, then do conjugate transpose */
				/* otherwise, do array transpose */
#endif
)
{

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

    Int i, j, k, p, bp, newj, do_values ;
#ifdef COMPLEX
    Int split ;
#endif

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

#ifndef NDEBUG
    Int nz ;
    ASSERT (n_col >= 0) ;
    nz = (Ap != (Int *) NULL) ? Ap [n_col] : 0 ;
    DEBUG2 (("UMF_transpose: "ID"-by-"ID" nz "ID"\n", n_row, n_col, nz)) ;
#endif

    if (check)
    {
	/* UMFPACK_symbolic skips this check */
	/* UMFPACK_transpose always does this check */
	if (!Ai || !Ap || !Ri || !Rp || !W)
	{
	    return (UMFPACK_ERROR_argument_missing) ;
	}
	if (n_row <= 0 || n_col <= 0)		/* n_row,n_col must be > 0 */
	{
	    return (UMFPACK_ERROR_n_nonpositive) ;
	}
	if (!UMF_is_permutation (P, W, n_row, n_row) ||
	    !UMF_is_permutation (Q, W, nq, nq))
	{
	    return (UMFPACK_ERROR_invalid_permutation) ;
	}
	if (AMD_valid (n_row, n_col, Ap, Ai) != AMD_OK)
	{
	    return (UMFPACK_ERROR_invalid_matrix) ;
	}
    }

#ifndef NDEBUG
    DEBUG2 (("UMF_transpose, input matrix:\n")) ;
    UMF_dump_col_matrix (Ax,
#ifdef COMPLEX
	Az,
#endif
	Ai, Ap, n_row, n_col, nz) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* count the entries in each row of A */
    /* ---------------------------------------------------------------------- */

    /* use W as workspace for RowCount */

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

    if (Q != (Int *) NULL)
    {
	for (newj = 0 ; newj < nq ; newj++)
	{
	    j = Q [newj] ;
	    ASSERT (j >= 0 && j < n_col) ;
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		i = Ai [p] ;
		ASSERT (i >= 0 && i < n_row) ;
		W [i]++ ;
	    }
	}
    }
    else
    {
	for (j = 0 ; j < n_col ; j++)
	{
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		i = Ai [p] ;
		ASSERT (i >= 0 && i < n_row) ;
		W [i]++ ;
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* compute the row pointers for R = A (P,Q) */
    /* ---------------------------------------------------------------------- */

    if (P != (Int *) NULL)
    {
	Rp [0] = 0 ;
	for (k = 0 ; k < n_row ; k++)
	{
	    i = P [k] ;
	    ASSERT (i >= 0 && i < n_row) ;
	    Rp [k+1] = Rp [k] + W [i] ;
	}
	for (k = 0 ; k < n_row ; k++)
	{
	    i = P [k] ;
	    ASSERT (i >= 0 && i < n_row) ;
	    W [i] = Rp [k] ;
	}
    }
    else
    {
	Rp [0] = 0 ;
	for (i = 0 ; i < n_row ; i++)
	{
	    Rp [i+1] = Rp [i] + W [i] ;
	}
	for (i = 0 ; i < n_row ; i++)
	{
	    W [i] = Rp [i] ;
	}
    }
    ASSERT (Rp [n_row] <= Ap [n_col]) ;

    /* at this point, W holds the permuted row pointers */

    /* ---------------------------------------------------------------------- */
    /* construct the row form of B */
    /* ---------------------------------------------------------------------- */

    do_values = Ax && Rx ;

#ifdef COMPLEX
    split = SPLIT (Az) && SPLIT (Rz) ;

    if (do_conjugate && do_values)
    {
	if (Q != (Int *) NULL)
	{
	    if (split)
	    {
		/* R = A (P,Q)' */
		for (newj = 0 ; newj < nq ; newj++)
		{
		    j = Q [newj] ;
		    ASSERT (j >= 0 && j < n_col) ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = newj ;
			Rx [bp] = Ax [p] ;
			Rz [bp] = -Az [p] ;
		    }
		}
	    }
	    else
	    {
		/* R = A (P,Q)' (merged complex values) */
		for (newj = 0 ; newj < nq ; newj++)
		{
		    j = Q [newj] ;
		    ASSERT (j >= 0 && j < n_col) ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = newj ;
			Rx [2*bp] = Ax [2*p] ;
			Rx [2*bp+1] = -Ax [2*p+1] ;
		    }
		}
	    }
	}
	else
	{
	    if (split)
	    {
		/* R = A (P,:)' */
		for (j = 0 ; j < n_col ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = j ;
			Rx [bp] = Ax [p] ;
			Rz [bp] = -Az [p] ;
		    }
		}
	    }
	    else
	    {
		/* R = A (P,:)' (merged complex values) */
		for (j = 0 ; j < n_col ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = j ;
			Rx [2*bp] = Ax [2*p] ;
			Rx [2*bp+1] = -Ax [2*p+1] ;
		    }
		}
	    }
	}
    }
    else
#endif
    {
	if (Q != (Int *) NULL)
	{
	    if (do_values)
	    {
#ifdef COMPLEX
		if (split)
#endif
		{
		    /* R = A (P,Q).' */
		    for (newj = 0 ; newj < nq ; newj++)
		    {
			j = Q [newj] ;
			ASSERT (j >= 0 && j < n_col) ;
			for (p = Ap [j] ; p < Ap [j+1] ; p++)
			{
			    bp = W [Ai [p]]++ ;
			    Ri [bp] = newj ;
			    Rx [bp] = Ax [p] ;
#ifdef COMPLEX
			    Rz [bp] = Az [p] ;
#endif
			}
		    }
		}
#ifdef COMPLEX
		else
		{
		    /* R = A (P,Q).' (merged complex values) */
		    for (newj = 0 ; newj < nq ; newj++)
		    {
			j = Q [newj] ;
			ASSERT (j >= 0 && j < n_col) ;
			for (p = Ap [j] ; p < Ap [j+1] ; p++)
			{
			    bp = W [Ai [p]]++ ;
			    Ri [bp] = newj ;
			    Rx [2*bp] = Ax [2*p] ;
			    Rx [2*bp+1] = Ax [2*p+1] ;
			}
		    }
		}
#endif
	    }
	    else
	    {
		/* R = pattern of A (P,Q).' */
		for (newj = 0 ; newj < nq ; newj++)
		{
		    j = Q [newj] ;
		    ASSERT (j >= 0 && j < n_col) ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			Ri [W [Ai [p]]++] = newj ;
		    }
		}
	    }
	}
	else
	{
	    if (do_values)
	    {
#ifdef COMPLEX
		if (split)
#endif
		{
		    /* R = A (P,:).' */
		    for (j = 0 ; j < n_col ; j++)
		    {
			for (p = Ap [j] ; p < Ap [j+1] ; p++)
			{
			    bp = W [Ai [p]]++ ;
			    Ri [bp] = j ;
			    Rx [bp] = Ax [p] ;
#ifdef COMPLEX
			    Rz [bp] = Az [p] ;
#endif
			}
		    }
		}
#ifdef COMPLEX
		else
		{
		    /* R = A (P,:).' (merged complex values) */
		    for (j = 0 ; j < n_col ; j++)
		    {
			for (p = Ap [j] ; p < Ap [j+1] ; p++)
			{
			    bp = W [Ai [p]]++ ;
			    Ri [bp] = j ;
			    Rx [2*bp] = Ax [2*p] ;
			    Rx [2*bp+1] = Ax [2*p+1] ;
			}
		    }
		}
#endif
	    }
	    else
	    {
		/* R = pattern of A (P,:).' */
		for (j = 0 ; j < n_col ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			Ri [W [Ai [p]]++] = j ;
		    }
		}
	    }
	}
    }

#ifndef NDEBUG
    for (k = 0 ; k < n_row ; k++)
    {
	if (P != (Int *) NULL)
	{
	    i = P [k] ;
	}
	else
	{
	    i = k ;
	}
	DEBUG3 ((ID":  W[i] "ID" Rp[k+1] "ID"\n", i, W [i], Rp [k+1])) ;
	ASSERT (W [i] == Rp [k+1]) ;
    }
    DEBUG2 (("UMF_transpose, output matrix:\n")) ;
    UMF_dump_col_matrix (Rx,
#ifdef COMPLEX
	Rz,
#endif
	Ri, Rp, n_col, n_row, Rp [n_row]) ;
    ASSERT (AMD_valid (n_col, n_row, Rp, Ri) == AMD_OK) ;
#endif

    return (UMFPACK_OK) ;
}
