//------------------------------------------------------------------------------
// CHOLMOD/Tcov/t_ctest: test for COLAMD
//------------------------------------------------------------------------------

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

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

#include "cm.h"
#include "colamd.h"

//------------------------------------------------------------------------------
// ctest
//------------------------------------------------------------------------------

void ctest (cholmod_sparse *A)
{
    double knobs [COLAMD_KNOBS], knobs2 [COLAMD_KNOBS] ;
    Int *P, *Cp, *Ci, *Si, *Sp ;
    cholmod_sparse *C, *A2, *B, *S, *BT ;
    Int nrow, ncol, alen, ok, stats [COLAMD_STATS], i, p, trial ;
    size_t s ;

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

    printf ("\nCOLAMD test\n") ;

    if (A == NULL)
    {
        return ;
    }

    if (A->stype)
    {
        A2 = CHOLMOD(copy) (A, 0, 0, cm) ;
        B = A2 ;
    }
    else
    {
        A2 = NULL ;
        B = A ;
    }

    nrow = B->nrow ;
    ncol = B->ncol ;
    S = NULL ;

    //--------------------------------------------------------------------------
    // allocate workspace colamd
    //--------------------------------------------------------------------------

    P = CHOLMOD(malloc) (nrow+1, sizeof (Int), cm) ;

    COLAMD_set_defaults (knobs) ;
    COLAMD_set_defaults (knobs2) ;
    COLAMD_set_defaults (NULL) ;
    COLAMD_report (NULL) ;
    SYMAMD_report (NULL) ;

    alen = COLAMD_recommended (B->nzmax, ncol, nrow) ;
    C = CHOLMOD(allocate_sparse) (ncol, nrow, alen, TRUE, TRUE, 0,
            CHOLMOD_PATTERN + DTYPE, cm) ;
    Cp = C->p ;
    Ci = C->i ;

    //--------------------------------------------------------------------------
    // order with colamd
    //--------------------------------------------------------------------------

    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;
    CHOLMOD(print_sparse) (C, "C for colamd", cm) ;
    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, NULL, stats) ;
    COLAMD_report (stats) ;
    OK (ok) ;
    ok = stats [COLAMD_STATUS] ;
    ok = (ok == COLAMD_OK || ok == COLAMD_OK_BUT_JUMBLED) ;
    OK (ok) ;

    // permutation returned in C->p, if the ordering succeeded
    // make sure P obeys the constraints
    OK (CHOLMOD(print_perm) (Cp, nrow, nrow, "colamd perm", cm)) ;

    //--------------------------------------------------------------------------
    // with different dense thresholds
    //--------------------------------------------------------------------------

    printf ("\nall dense rows:\n") ;
    knobs2 [COLAMD_DENSE_ROW] = 0 ;
    knobs2 [COLAMD_DENSE_COL] = 0.5 ;
    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;
    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs2, stats) ;
    COLAMD_report (stats) ;
    OK (CHOLMOD(print_perm) (Cp, nrow, nrow, "colamd perm", cm)) ;

    printf ("\nall dense cols:\n") ;
    knobs2 [COLAMD_DENSE_ROW] = 0.5 ;
    knobs2 [COLAMD_DENSE_COL] = 0 ;
    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;
    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs2, stats) ;
    COLAMD_report (stats) ;
    OK (CHOLMOD(print_perm) (Cp, nrow, nrow, "colamd perm", cm)) ;

    printf ("\nno dense rows/cols:\n") ;
    knobs2 [COLAMD_DENSE_ROW] = -1 ;
    knobs2 [COLAMD_DENSE_COL] = -1 ;
    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;
    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs2, stats) ;
    COLAMD_report (stats) ;
    OK (CHOLMOD(print_perm) (Cp, nrow, nrow, "colamd perm", cm)) ;

    knobs2 [COLAMD_DENSE_ROW] = 0.5 ;
    knobs2 [COLAMD_DENSE_COL] = 0.5 ;

    //--------------------------------------------------------------------------
    // duplicate entries
    //--------------------------------------------------------------------------

    if (ncol > 2 && nrow > 2)
    {
        ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
        OK (ok) ;
        if (Cp [1] - Cp [0] > 2)
        {
            Ci [0] = Ci [1] ;
        }
        ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs2, stats) ;
        COLAMD_report (stats) ;
        OK (CHOLMOD(print_perm) (Cp, nrow, nrow, "colamd perm", cm)) ;
    }

    //--------------------------------------------------------------------------
    // symamd
    //--------------------------------------------------------------------------

    if (nrow == ncol)
    {
        Int n = nrow ;

        BT = CHOLMOD(transpose) (B, 0, cm) ;
        OKP(BT);
        S = CHOLMOD(add) (B, BT, one, one, FALSE, FALSE, cm) ;
        CHOLMOD(free_sparse) (&BT, cm) ;
        Si = S->i ;
        Sp = S->p ;

        void * (*calloc_func) (size_t, size_t) ;
        void (*free_func) (void *) ;
        calloc_func = SuiteSparse_config_calloc_func_get ( ) ;
        free_func   = SuiteSparse_config_free_func_get ( ) ;

        ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
        OK (ok) ;
        OK (CHOLMOD(print_perm) (P, n, n, "symamd perm", cm)) ;
        SYMAMD_report (stats) ;

        //----------------------------------------------------------------------
        // symamd errors
        //----------------------------------------------------------------------

        test_memory_handler ( ) ;
        calloc_func = SuiteSparse_config_calloc_func_get ( ) ;
        free_func   = SuiteSparse_config_free_func_get ( ) ;
        for (trial = 0 ; trial < 3 ; trial++)
        {
            my_tries = trial ;
            ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
            NOT (ok) ;
        }
        my_tries = 3 ;
        ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
        OK (ok) ;
        normal_memory_handler ( ) ;
        calloc_func = SuiteSparse_config_calloc_func_get ( ) ;
        free_func   = SuiteSparse_config_free_func_get ( ) ;

        ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, NULL,
                calloc_func, free_func) ;
                NOT (ok);

        ok = SYMAMD_MAIN (n, NULL, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
        SYMAMD_report (stats) ;

        ok = SYMAMD_MAIN (n, Si, NULL, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
        SYMAMD_report (stats) ;

        ok = SYMAMD_MAIN (-1, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
        SYMAMD_report (stats) ;

        p = Sp [n] ;
        Sp [n] = -1 ;
        ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
        SYMAMD_report (stats) ;
        Sp [n] = p ;

        Sp [0] = -1 ;
        ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
        SYMAMD_report (stats) ;
        Sp [0] = 0 ;

        if (n > 2 && Sp [n] > 3)
        {
            p = Sp [1] ;
            Sp [1] = -1 ;
            ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
            SYMAMD_report (stats) ;
            Sp [1] = p ;

            i = Si [0] ;
            Si [0] = -1 ;
            ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
            SYMAMD_report (stats) ;
            Si [0] = i ;

            // ok, but jumbled
            i = Si [0] ;
            Si [0] = Si [1] ;
            Si [1] = i ;
            ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                OK (ok);
            SYMAMD_report (stats) ;
            OK (CHOLMOD(print_perm) (P, nrow, nrow, "symamd perm", cm)) ;
            i = Si [0] ;
            Si [0] = Si [1] ;
            Si [1] = i ;

            test_memory_handler ( ) ;
            calloc_func = SuiteSparse_config_calloc_func_get ( ) ;
            free_func   = SuiteSparse_config_free_func_get ( ) ;
            ok = SYMAMD_MAIN (n, Si, Sp, P, NULL, stats,
                calloc_func, free_func) ;
                NOT (ok);
            SYMAMD_report (stats) ;
            normal_memory_handler ( ) ;
            calloc_func = SuiteSparse_config_calloc_func_get ( ) ;
            free_func   = SuiteSparse_config_free_func_get ( ) ;
        }
    }

    //--------------------------------------------------------------------------
    // error tests
    //--------------------------------------------------------------------------

    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;
    ok = COLAMD_MAIN (ncol, nrow, 0, Ci, Cp, knobs, stats) ;
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = COLAMD_MAIN (ncol, nrow, alen, NULL, Cp, knobs, stats);
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, NULL, knobs, stats);
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs, NULL) ;
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = COLAMD_MAIN (-1, nrow, alen, Ci, Cp, knobs, stats) ;
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = COLAMD_MAIN (ncol, -1, alen, Ci, Cp, knobs, stats) ;
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;
    Cp [nrow] = -1 ;
    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs, stats) ;
    NOT (ok) ;
    COLAMD_report (stats) ;

    Cp [0] = 1 ;
    ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs, stats) ;
    NOT (ok) ;
    COLAMD_report (stats) ;

    ok = CHOLMOD(transpose_unsym) (B, 0, NULL, NULL, 0, C, cm) ;
    OK (ok) ;

    if (nrow > 0 && alen > 0 && Cp [1] > 0)
    {

        p = Cp [1] ;
        Cp [1] = -1 ;
        ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs, stats) ;
        NOT(ok);
        COLAMD_report (stats) ;
        Cp [1] = p ;

        i = Ci [0] ;
        Ci [0] = -1 ;
        ok = COLAMD_MAIN (ncol, nrow, alen, Ci, Cp, knobs, stats) ;
        NOT(ok);
        COLAMD_report (stats) ;
        Ci [0] = i ;
    }

    s = COLAMD_recommended (-1, 0, 0) ;
    OK (s == 0) ;

    //--------------------------------------------------------------------------
    // free workspace
    //--------------------------------------------------------------------------

    CHOLMOD(free) (nrow+1, sizeof (Int), P, cm) ;
    CHOLMOD(free_sparse) (&S, cm) ;
    CHOLMOD(free_sparse) (&A2, cm) ;
    CHOLMOD(free_sparse) (&C, cm) ;
}

