#include "sparse_matrix_utils.h"
#include "float_utils.h"
#include "matrix.h"


sparse_matrix_t *sparse_matrix_new_from_matrix(double_matrix_t *matrix) {
    sparse_matrix_t *sparse = sparse_matrix_new_shape(matrix->m, matrix->n);

    for (size_t i = 0; i < matrix->m; i++) {
        for (size_t j = 0; j < matrix->n; j++) {    
            double value = double_matrix_get(matrix, i, j);
            if (!double_equals(value, 0.0)) {
                sparse_matrix_append(sparse, j, value);
            }
        }
        sparse_matrix_finalize_row(sparse);
    }
    return sparse;
}


bool sparse_matrix_add_unique_columns(sparse_matrix_t *matrix, khash_t(int_uint32) *unique_columns, uint32_array *array) {
    size_t n = matrix->indices->n;
    uint32_t *indices = matrix->indices->a;

    kh_clear(int_uint32, unique_columns);

    size_t i;
    khiter_t k;

    for (i = 0; i < n; i++) {
        uint32_t col = indices[i];

        int ret = 0;
        k = kh_get(int_uint32, unique_columns, col);  
        if (k == kh_end(unique_columns)) {
            uint32_t next_id = (uint32_t)kh_size(unique_columns);

            k = kh_put(int_uint32, unique_columns, col, &ret);
            if (ret < 0) {
                return false;
            }
            kh_value(unique_columns, k) = next_id;
        }

    }

    uint32_array_clear(array);
    if (!uint32_array_resize_fixed(array, kh_size(unique_columns))) {
        return false;
    }

    khint_t key;

    uint32_t *batch = array->a;
    uint32_t col_id;

    kh_foreach(unique_columns, key, col_id, {
        batch[col_id] = (uint32_t)key;
    })

    return true;
}

bool sparse_matrix_alias_columns(sparse_matrix_t *matrix, khash_t(int_uint32) *unique_columns) {
    size_t n = matrix->indices->n;
    uint32_t *indices = matrix->indices->a;

    size_t i;
    khiter_t k;
    uint32_t col_id;

    for (i = 0; i < n; i++) {
        uint32_t col = indices[i];

        int ret = 0;
        k = kh_get(int_uint32, unique_columns, col);  
        if (k != kh_end(unique_columns)) {
            col_id = kh_value(unique_columns, k);
            indices[i] = col_id;
        } else {
            return false;
        }
    }

    matrix->n = kh_size(unique_columns);

    return true;
}

inline bool sparse_matrix_add_unique_columns_alias(sparse_matrix_t *matrix, khash_t(int_uint32) *unique_columns, uint32_array *array) {
    return sparse_matrix_add_unique_columns(matrix, unique_columns, array) &&
           sparse_matrix_alias_columns(matrix, unique_columns);
}

uint32_array *sparse_matrix_unique_columns(sparse_matrix_t *matrix) {
    khash_t(int_uint32) *unique_columns = kh_init(int_uint32);
    uint32_array *ret = uint32_array_new();

    if (sparse_matrix_add_unique_columns(matrix, unique_columns, ret)) {
        kh_destroy(int_uint32, unique_columns);
        return ret;
    }

    kh_destroy(int_uint32, unique_columns);
    uint32_array_destroy(ret);
    return NULL;
}
