// =============================================================== //
//                                                                 //
//   File      : AP_matrix.cxx                                     //
//   Purpose   :                                                   //
//                                                                 //
//   Institute of Microbiology (Technical University Munich)       //
//   http://www.arb-home.de/                                       //
//                                                                 //
// =============================================================== //

#include "AP_matrix.hxx"

#include <arbdbt.h>
#include <aw_window.hxx>
#include <aw_root.hxx>
#include <aw_awar.hxx>
#include <cfloat>

#define ap_assert(cond) arb_assert(cond)

// ------------------
//      AP_matrix

AP_matrix::AP_matrix(long si) {
    ARB_calloc(m, si);
    for (long i=0; i<si; i++) {
        ARB_calloc(m[i], si);
    }
    size = si;
}

AP_matrix::~AP_matrix() {
    for (long i=0; i<size; i++) {
        free(m[i]);
    }
    free(m);
}

// ---------------------------
//      AP_userdef_matrix

void AP_userdef_matrix::set_desc(char**& which_desc, int idx, const char *desc) {
    if (!which_desc) ARB_calloc(which_desc, get_size());
    which_desc[idx] = strdup(desc);
}

void AP_userdef_matrix::create_awars(AW_root *awr) {
    char buffer[1024];
    int x, y;
    for (x = 0; x<get_size(); x++) {
        if (x_description[x]) {
            for (y = 0; y<get_size(); y++) {
                if (y_description[y]) {
                    sprintf(buffer, "%s/B%s/B%s", awar_prefix, x_description[x], y_description[y]);
                    if (x==y) {
                        awr->awar_float(buffer, 0)->set_minmax(0.0, 2.0);
                    }
                    else {
                        awr->awar_float(buffer, 1.0)->set_minmax(0.0, 2.0);
                    }
                }

            }
        }
    }
}
void AP_userdef_matrix::update_from_awars(AW_root *awr) {
    char buffer[1024];
    int x, y;
    for (x = 0; x<get_size(); x++) {
        if (x_description[x]) {
            for (y = 0; y<get_size(); y++) {
                if (y_description[y]) {
                    sprintf(buffer, "%s/B%s/B%s", awar_prefix, x_description[x], y_description[y]);
                    this->set(x, y, awr->awar(buffer)->read_float());
                }
            }
        }
    }
}

void AP_userdef_matrix::create_input_fields(AW_window *aww) {
    char buffer[1024];
    int x, y;
    aww->create_button(NULp, "    ");
    for (x = 0; x<get_size(); x++) {
        if (x_description[x]) {
            aww->create_button(NULp, x_description[x]);
        }
    }
    aww->at_newline();
    for (x = 0; x<get_size(); x++) {
        if (x_description[x]) {
            aww->create_button(NULp, x_description[x]);
            for (y = 0; y<get_size(); y++) {
                if (y_description[y]) {
                    sprintf(buffer, "%s/B%s/B%s", awar_prefix, x_description[x], y_description[y]);
                    aww->create_input_field(buffer, 4);
                }
            }
            aww->at_newline();
        }
    }
}

void AP_userdef_matrix::normize() { // set values so that average of non diag elems == 1.0
    int x, y;
    double sum = 0.0;
    double elems = 0.0;
    for (x = 0; x<get_size(); x++) {
        if (x_description[x]) {
            for (y = 0; y<get_size(); y++) {
                if (y!=x && y_description[y]) {
                    sum += this->get(x, y);
                    elems += 1.0;
                }
            }
        }
    }
    if (sum == 0.0) return;
    sum /= elems;
    for (x = 0; x<get_size(); x++) {
        for (y = 0; y<get_size(); y++) { // LOOP_VECTORIZED
            this->set(x, y, get(x, y)/sum);
        }
    }
}

AP_userdef_matrix::~AP_userdef_matrix() {
    for (long i=0; i<get_size(); i++) {
        if (x_description) free(x_description[i]);
        if (y_description) free(y_description[i]);
    }
    free(x_description);
    free(y_description);
    free(awar_prefix);
}

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

#ifdef UNIT_TESTS
#ifndef TEST_UNIT_H
#include <test_unit.h>
#endif

void TEST_AP_smatrix() {
    // TEST_REJECT(true); // let it fail!

    const int  SIZE = 10;
    AP_smatrix m(SIZE);

    TEST_EXPECT_EQUAL(m.size(), SIZE);
    TEST_EXPECT_EQUAL(m.fast_get(9, 8), 0.0);

    for (int i = 0; i<SIZE; ++i) {
        for (int j = 0; j<=i; ++j) {
            m.set(i, j, j>0 ? AP_FLOAT(i)/j : i);
            TEST_EXPECT_EQUAL(m.get(i, j), m.get(j, i));
        }
    }

    TEST_EXPECT_EQUAL(m.get_max_value(), 9.0);
    TEST_EXPECT_EQUAL(m.get(9, 8), 9.0/8.0);
}

#endif // UNIT_TESTS

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