//------------------------------------------------------------------------------
// CHOLMOD/Tcov/t_memory: memory-failure testing in CHOLMOD
//------------------------------------------------------------------------------

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

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

// Extensive memory-failure testing for CHOLMOD.
//
// my_malloc2, my_calloc2, and my_realloc2 pretend to fail if my_tries goes to
// zero, to test CHOLMOD's memory error handling.   No failure occurs if
// my_tries is negative.

#include "cm.h"

//------------------------------------------------------------------------------
// my_tries
//------------------------------------------------------------------------------

int64_t my_tries = -1 ; // a global variable

//------------------------------------------------------------------------------
// my_malloc2
//------------------------------------------------------------------------------

void *my_malloc2 (size_t size)
{
    void *p ;
    if (my_tries == 0)
    {
        // pretend to fail
        return (NULL) ;
    }
    if (my_tries > 0)
    {
        my_tries-- ;
    }
    p = malloc (size) ;
    return (p) ;
}

//------------------------------------------------------------------------------
// my_calloc2
//------------------------------------------------------------------------------

void *my_calloc2 (size_t n, size_t size)
{
    void *p ;
    if (my_tries == 0)
    {
        // pretend to fail
        return (NULL) ;
    }
    if (my_tries > 0)
    {
        my_tries-- ;
    }
    p = calloc (n, size) ;
    return (p) ;
}

//------------------------------------------------------------------------------
// my_realloc2
//------------------------------------------------------------------------------

void *my_realloc2 (void *p, size_t size)
{
    void *p2 ;
    if (my_tries == 0)
    {
        // pretend to fail
        return (NULL) ;
    }
    if (my_tries > 0)
    {
        my_tries-- ;
    }
    p2 = realloc (p, size) ;
    return (p2) ;
}

//------------------------------------------------------------------------------
// my_free2
//------------------------------------------------------------------------------

void my_free2 (void *p)
{
    free (p) ;
}

//------------------------------------------------------------------------------
// normal_memory_handler
//------------------------------------------------------------------------------

void normal_memory_handler ( void )
{
    SuiteSparse_config_malloc_func_set (malloc) ;
    SuiteSparse_config_calloc_func_set (calloc) ;
    SuiteSparse_config_realloc_func_set (realloc) ;
    SuiteSparse_config_free_func_set (free) ;

    cm->error_handler = my_handler ;
    CHOLMOD(free_work) (cm) ;
}

//------------------------------------------------------------------------------
// test_memory_handler
//------------------------------------------------------------------------------

void test_memory_handler ( void )
{
    SuiteSparse_config_malloc_func_set (my_malloc2) ;
    SuiteSparse_config_calloc_func_set (my_calloc2) ;
    SuiteSparse_config_realloc_func_set (my_realloc2) ;
    SuiteSparse_config_free_func_set (my_free2) ;

    cm->error_handler = NULL ;
    CHOLMOD(free_work) (cm) ;
    my_tries = 0 ;
}

//------------------------------------------------------------------------------
// memory tests
//------------------------------------------------------------------------------

void memory_tests (cholmod_triplet *T)
{
    double err ;
    cholmod_sparse *A ;
    Int trial ;
    size_t count, inuse ;
    const int psave = cm->print ;

    test_memory_handler ( ) ;
    inuse = cm->memory_inuse ;

    cm->nmethods = 8 ;
    cm->print = 0 ;
    cm->final_resymbol = TRUE ;

    cm->final_asis = FALSE ;
    cm->final_super = FALSE ;
    cm->final_ll = FALSE ;
    cm->final_pack = FALSE ;
    cm->final_monotonic = FALSE ;

    //--------------------------------------------------------------------------
    // test raw factorizations
    //--------------------------------------------------------------------------

    printf ("==================================== fac memory test\n") ;
    count = cm->malloc_count ;
    my_tries = -1 ;
    for (trial = 0 ; my_tries <= 0 ; trial++)
    {
        cm->print = 0 ;
        fflush (stdout) ;
        my_tries = trial ;
        A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ;
        my_srand (trial+1) ;                                    // RAND reset
        err = raw_factor (A, FALSE) ;                           // RAND
        CHOLMOD(free_sparse) (&A, cm) ;
        OK (CHOLMOD(print_common) ("7:cm", cm)) ;
        CHOLMOD(free_work) (cm) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }
    CHOLMOD(free_work) (cm) ;
    printf ("memory test: fac error %.1g trials "ID"\n", err, trial) ;
    printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
    printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
    OK (count == cm->malloc_count) ;
    OK (inuse == cm->memory_inuse) ;

    //--------------------------------------------------------------------------
    // test raw factorizations (rowfac_mask)
    //--------------------------------------------------------------------------

    printf ("==================================== fac memory test2\n") ;
    count = cm->malloc_count ;
    my_tries = -1 ;
    for (trial = 0 ; my_tries <= 0 ; trial++)
    {
        cm->print = 0 ;
        fflush (stdout) ;
        my_tries = trial ;
        A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ;
        my_srand (trial+1) ;                                    // RAND reset
        err = raw_factor2 (A, 0., 0) ;                          // RAND
        CHOLMOD(free_sparse) (&A, cm) ;
        OK (CHOLMOD(print_common) ("8:cm", cm)) ;
        CHOLMOD(free_work) (cm) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }
    CHOLMOD(free_work) (cm) ;
    printf ("memory test: fac error %.1g trials "ID"\n", err, trial) ;
    printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
    printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
    OK (count == cm->malloc_count) ;
    OK (inuse == cm->memory_inuse) ;

    //--------------------------------------------------------------------------
    // test augmented system solver
    //--------------------------------------------------------------------------

    printf ("==================================== aug memory test\n") ;
    count = cm->malloc_count ;
    my_tries = -1 ;
    for (trial = 0 ; my_tries <= 0 ; trial++)
    {
        cm->print = 0 ;
        fflush (stdout) ;
        my_tries = trial ;
        A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ;
        err = aug (A) ;                         // no random number use
        CHOLMOD(free_sparse) (&A, cm) ;
        OK (CHOLMOD(print_common) ("9:cm", cm)) ;
        CHOLMOD(free_work) (cm) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }
    CHOLMOD(free_work) (cm) ;
    printf ("memory test: aug error %.1g trials "ID"\n", err, trial) ;
    printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
    printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
    OK (count == cm->malloc_count) ;
    OK (inuse == cm->memory_inuse) ;

    //--------------------------------------------------------------------------
    // test ops
    //--------------------------------------------------------------------------

    printf ("==================================== test_ops memory test\n") ;
    count = cm->malloc_count ;
    my_tries = -1 ;
    for (trial = 0 ; my_tries <= 0 ; trial++)
    {
        cm->print = 0 ;
        fflush (stdout) ;
        my_tries = trial ;
        A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ;
        my_srand (trial+1) ;                                    // RAND reset
        err = test_ops (A) ;                                    // RAND
        CHOLMOD(free_sparse) (&A, cm) ;
        OK (CHOLMOD(print_common) ("10:cm", cm)) ;
        CHOLMOD(free_work) (cm) ;
        printf ("inuse "ID" "ID"\n", (Int) inuse, (Int) (cm->memory_inuse)) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }
    printf ("memory test: testops error %.1g trials "ID"\n", err, trial) ;
    printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
    printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
    OK (count == cm->malloc_count) ;
    OK (inuse == cm->memory_inuse) ;

    //--------------------------------------------------------------------------
    // test lpdemo
    //--------------------------------------------------------------------------

    if (T == NULL || T->nrow != T->ncol)
    {
        printf ("==================================== lpdemo memory test\n") ;
        count = cm->malloc_count ;
        my_tries = -1 ;
        for (trial = 0 ; my_tries <= 0 ; trial++)
        {
            cm->print = 0 ;
            fflush (stdout) ;
            my_tries = trial ;
            my_srand (trial+1) ;                                // RAND reset
            err = lpdemo (T) ;                                  // RAND
            OK (CHOLMOD(print_common) ("11:cm", cm)) ;
            CHOLMOD(free_work) (cm) ;
            OK (count == cm->malloc_count) ;
            OK (inuse == cm->memory_inuse) ;
        }
        CHOLMOD(free_work) (cm) ;
        printf ("memory test: lpdemo error %.1g trials "ID"\n", err, trial) ;
        printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
        printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }

    //--------------------------------------------------------------------------
    // test solver
    //--------------------------------------------------------------------------

    printf ("==================================== solve memory test\n") ;
    count = cm->malloc_count ;
    my_tries = -1 ;
    for (trial = 0 ; my_tries <= 0 ; trial++)
    {
        CHOLMOD(defaults) (cm) ; cm->useGPU = 0 ;
        cm->supernodal = CHOLMOD_SUPERNODAL ;
        cm->metis_memory = 2.0 ;
        cm->nmethods = 4 ;
        cm->print = 0 ;
        fflush (stdout) ;
        my_tries = trial ;
        A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ;
        my_srand (trial+1) ;                                    // RAND reset
        err = solve (A) ;                                       // RAND
        CHOLMOD(free_sparse) (&A, cm) ;
        OK (CHOLMOD(print_common) ("12:cm", cm)) ;
        CHOLMOD(free_work) (cm) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }
    CHOLMOD(free_work) (cm) ;
    printf ("memory test: solve error %.1g trials "ID"\n", err, trial) ;
    printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
    printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
    OK (count == cm->malloc_count) ;
    OK (inuse == cm->memory_inuse) ;
    cm->supernodal = CHOLMOD_AUTO ;

    //--------------------------------------------------------------------------
    // cat tests
    //--------------------------------------------------------------------------

    CHOLMOD(defaults) (cm) ; cm->useGPU = 0 ;
    test_memory_handler ( ) ;

    printf ("==================================== cat_tests memory test\n") ;
    count = cm->malloc_count ;
    my_tries = -1 ;
    for (trial = 0 ; my_tries <= 0 ; trial++)
    {
        cm->print = 0 ;
        fflush (stdout) ;
        my_tries = trial ;
        A = CHOLMOD(triplet_to_sparse) (T, 0, cm) ;
        err = cat_tests (A, cm) ;
        CHOLMOD(free_sparse) (&A, cm) ;
        CHOLMOD(free_work) (cm) ;
        printf ("inuse "ID" "ID"\n", (Int) inuse, (Int) (cm->memory_inuse)) ;
        OK (count == cm->malloc_count) ;
        OK (inuse == cm->memory_inuse) ;
    }
    printf ("memory test: cat_test error %.1g trials "ID"\n", err, trial) ;
    printf ("initial count: "ID" final count "ID"\n",
            (Int) count, (Int) cm->malloc_count) ;
    printf ("initial inuse: "ID" final inuse "ID"\n",
            (Int) inuse, (Int) cm->memory_inuse) ;
    OK (count == cm->malloc_count) ;
    OK (inuse == cm->memory_inuse) ;

    //--------------------------------------------------------------------------
    // restore original memory handler
    //--------------------------------------------------------------------------

    progress (1, '|') ;
    normal_memory_handler ( ) ;
    cm->print = psave ;

    printf ("All memory tests OK, no error\n") ;
}

