/**
 *
 * @file plugins/MatrixVisualizer/Formats/SymbolMatrix.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Arthur Chevalier
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 *
 * @date 2024-07-17
 */
#include "SymbolMatrix.hpp"

#include "../Helper.hpp"

#include <cstdlib>
#include <cstring>
#include <cmath>

static inline int32_t imin(int32_t a, int32_t b) {
    return (a < b) ? a : b;
}
static inline int32_t imax(int32_t a, int32_t b) {
    return (a > b) ? a : b;
}

/* Macros */
#define memFree(ptr) free((void *)(ptr))
#define memFree_null(ptr) \
    do {                  \
        memFree(ptr);     \
        (ptr) = nullptr;  \
    } while (0)

void symbol_matrix_init(symbol_matrix_t *matrix) {
    memset(matrix, 0, sizeof(symbol_matrix_t));
    return;
}

void symbol_matrix_deinit(symbol_matrix_t *matrix) {
    if (matrix->m_sndetab != nullptr) {
        memFree_null(matrix->m_sndetab);
    }
    if (matrix->m_cblktab != nullptr) {
        memFree_null(matrix->m_cblktab);
    }
    if (matrix->m_bloktab != nullptr) {
        memFree_null(matrix->m_bloktab);
    }
    if (matrix->m_browtab != nullptr) {
        memFree_null(matrix->m_browtab);
    }

    memset(matrix, 0, sizeof(symbol_matrix_t));

    return;
}

#include <iostream>

void symbol_matrix_print_stats(symbol_matrix_t *matrix) {
    symbol_cblk_t *cblk;
    symbol_blok_t *blok;
    int32_t itercblk, dof;
    int32_t sndenbr, cblknbr, bloknbr;
    int32_t cblkmin, cblkmax;
    int32_t blokmin, blokmax;
    double cblkavg1, blokavg1;
    double cblkavg2, blokavg2;
    size_t mem = 0;

    sndenbr = matrix->m_sndenbr;
    cblknbr = matrix->m_cblknbr;
    bloknbr = matrix->m_bloknbr;
    cblkmin = INT32_MAX;
    cblkmax = 0;
    cblkavg1 = 0.0;
    cblkavg2 = 0.0;
    blokmin = INT32_MAX;
    blokmax = 0;
    blokavg1 = 0.0;
    blokavg2 = 0.0;

    cblk = matrix->m_cblktab;
    blok = matrix->m_bloktab;

    for (itercblk = 0; itercblk < cblknbr; ++itercblk, ++cblk) {
        int32_t iterblok = cblk[0].m_bloknum + 1;
        int32_t lbloknum = cblk[1].m_bloknum;

        int32_t colnbr = cblk->m_lcolnum - cblk->m_fcolnum + 1;

        cblkmin = imin(cblkmin, colnbr);
        cblkmax = imax(cblkmax, colnbr);
        cblkavg1 += colnbr;
        cblkavg2 += colnbr * colnbr;
        blok++;

        /* Only extra diagonal */
        for (; iterblok < lbloknum; ++iterblok, ++blok) {
            int32_t rownbr = blok->m_lrownum - blok->m_frownum + 1;

            blokmin = imin(blokmin, rownbr);
            blokmax = imax(blokmax, rownbr);
            blokavg1 += rownbr;
            blokavg2 += rownbr * rownbr;
        }
    }

    dof = matrix->m_dof;

    if (dof < 1) {
        Helper::log(LogStatus::WARNING, "DOF lower than 1 so statistics might be wrong !");
        dof = 1;
    }

    blokmin *= dof;
    blokmax *= dof;
    cblkmin *= dof;
    cblkmax *= dof;
    cblkavg1 = (cblkavg1 * (double)dof) / (double)cblknbr;
    blokavg1 = (blokavg1 * (double)dof) / (double)bloknbr;
    cblkavg2 = sqrt(((cblkavg2 * (double)dof * (double)dof) / (double)cblknbr) - cblkavg1 * cblkavg1);
    blokavg2 = sqrt(((blokavg2 * (double)dof * (double)dof) / (double)bloknbr) - blokavg1 * blokavg1);

    /* Compute symbol matrix space */
    mem = sizeof(symbol_matrix_t);
    mem += sizeof(symbol_cblk_t) * (cblknbr + 1);
    mem += sizeof(symbol_blok_t) * matrix->m_bloknbr;
    mem += sizeof(int32_t) * bloknbr;

    Helper::set_infos(
        "Symbol Matrix statistics:\n"
        "      Number of superndoes              %10ld\n"
        "      Number of cblk                    %10ld\n"
        "      Number of blok                    %10ld\n"
        "      Cblk width min                    %10ld\n"
        "      Cblk width max                    %10ld\n"
        "      Cblk width avg                 %11.2lf\n"
        "      Cblk width stdev               %11.2lf\n"
        "      Blok height min                   %10ld\n"
        "      Blok height max                   %10ld\n"
        "      Blok height avg                %11.2lf\n"
        "      Blok height stdev              %11.2lf\n"
        "      Matrix structure space         %11.2lf %cB\n",
        (long)sndenbr, (long)cblknbr, (long)bloknbr,
        (long)cblkmin, (long)cblkmax, cblkavg1, cblkavg2,
        (long)blokmin, (long)blokmax, blokavg1, blokavg2,
        print_get_value(mem), print_get_units(mem));
}
