//------------------------------------------------------------------------------
// CHOLMOD/MATLAB/lxbpattern: MATLAB interface for CHOLMOD symbolic x=L\b solve
//------------------------------------------------------------------------------

// CHOLMOD/MATLAB Module.  Copyright (C) 2005-2023, Timothy A. Davis.
// All Rights Reserved.
// SPDX-License-Identifier: GPL-2.0+

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

// Find the nonzero pattern of x=L\b for a sparse vector b.  If numerical
// cancellation has caused entries to drop in L, then this function may
// give an incorrect result.
//
// xpattern = lxbpattern (L,b), same as xpattern = find (L\b),
// assuming no numerical cancellation, except that xpattern will not
// appear in sorted ordering (it appears in topological ordering).
//
// The core cholmod_lsolve_pattern function takes O(length (xpattern)) time,
// except that the initialzations in this mexFunction interface add O(n) time.
//
// This function is for testing cholmod_lsolve_pattern only.

#include "sputil2.h"

void mexFunction
(
    int nargout,
    mxArray *pargout [ ],
    int nargin,
    const mxArray *pargin [ ]
)
{
    double dummy = 0 ;
    int64_t *Lp, *Lnz, *Xp, *Xi, xnz ;
    cholmod_sparse *B, Bmatrix, *X ;
    cholmod_factor *L ;
    cholmod_common Common, *cm ;
    int64_t j, n ;

    //--------------------------------------------------------------------------
    // start CHOLMOD and set parameters
    //--------------------------------------------------------------------------

    cm = &Common ;
    cholmod_l_start (cm) ;
    sputil2_config (SPUMONI, cm) ;

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

    if (nargin != 2 || nargout > 1)
    {
        mexErrMsgTxt ("usage: xpattern = lxbpattern (L,b)") ;
    }

    n = mxGetN (pargin [0]) ;

    if (!mxIsSparse (pargin [0]) || n != mxGetM (pargin [0]))
    {
        mexErrMsgTxt ("lxbpattern: L must be sparse and square") ;
    }
    if (n != mxGetM (pargin [1]) || mxGetN (pargin [1]) != 1)
    {
        mexErrMsgTxt ("lxbpattern: b wrong dimension") ;
    }
    if (!mxIsSparse (pargin [1]))
    {
        mexErrMsgTxt ("lxbpattern: b must be sparse") ;
    }

    //--------------------------------------------------------------------------
    // get the sparse b
    //--------------------------------------------------------------------------

    // get sparse matrix B (unsymmetric)
    size_t B_xsize = 0 ;
    B = sputil2_get_sparse (pargin [1], 0, CHOLMOD_DOUBLE, &Bmatrix,
        &B_xsize, cm) ;

    //--------------------------------------------------------------------------
    // construct a shallow copy of the input sparse matrix L
    //--------------------------------------------------------------------------

    // the construction of the CHOLMOD takes O(n) time and memory

    // allocate the CHOLMOD symbolic L
    L = cholmod_l_allocate_factor (n, cm) ;
    L->ordering = CHOLMOD_NATURAL ;

    // get the MATLAB L
    L->p = mxGetJc (pargin [0]) ;
    L->i = mxGetIr (pargin [0]) ;
    L->x = mxGetData (pargin [0]) ;
    L->z = NULL ;

    // allocate and initialize the rest of L
    L->nz = cholmod_l_malloc (n, sizeof (int64_t), cm) ;
    Lp = L->p ;
    Lnz = L->nz ;
    for (j = 0 ; j < n ; j++)
    {
        Lnz [j] = Lp [j+1] - Lp [j] ;
    }

    // L is not truly a valid CHOLMOD sparse factor, since L->prev and next are
    //  NULL.  But these pointers are not accessed in cholmod_lsolve_pattern
    L->prev = NULL ;
    L->next = NULL ;

    L->xtype = (mxIsComplex (pargin [0])) ? CHOLMOD_COMPLEX : CHOLMOD_REAL ;
    L->nzmax = Lp [n] ;

    //--------------------------------------------------------------------------
    // allocate size-n space for the result X
    //--------------------------------------------------------------------------

    X = cholmod_l_allocate_sparse (n, 1, n, FALSE, TRUE, 0, 0, cm) ;

    //--------------------------------------------------------------------------
    // find the pattern of X=L\B
    //--------------------------------------------------------------------------

    cholmod_l_lsolve_pattern (B, L, X, cm) ;

    //--------------------------------------------------------------------------
    // return result, converting to 1-based
    //--------------------------------------------------------------------------

    Xp = (int64_t *) X->p ;
    Xi = (int64_t *) X->i ;
    xnz = Xp [1] ;
    pargout [0] = sputil2_put_int (Xi, xnz, 1) ;

    //--------------------------------------------------------------------------
    // free workspace and the CHOLMOD L, except for what is copied to MATLAB
    //--------------------------------------------------------------------------

    L->p = NULL ;
    L->i = NULL ;
    L->x = NULL ;
    L->z = NULL ;
    sputil2_free_sparse (&B, &Bmatrix, B_xsize, cm) ;
    cholmod_l_free_factor (&L, cm) ;
    cholmod_l_free_sparse (&X, cm) ;
    cholmod_l_finish (cm) ;
    if (SPUMONI > 0) cholmod_l_print_common (" ", cm) ;
}

