
/****************************************************************
 *
 * MODULE:     v.generalize
 *
 * AUTHOR(S):  Daniel Bundala
 *
 * PURPOSE:    Definition of a matrix and basic operations with
 *             matrices
 *
 * COPYRIGHT:  (C) 2002-2005 by the GRASS Development Team
 *
 *             This program is free software under the
 *             GNU General Public License (>=v2).
 *             Read the file COPYING that comes with GRASS
 *             for details.
 *
 ****************************************************************/

#include <string.h>
#include <grass/gis.h>
#include <grass/glocale.h>
#include "matrix.h"

int matrix_init(int rows, int cols, MATRIX *res)
{

    int i, j;

    res->rows = rows;
    res->cols = cols;
    res->a = (double **)G_calloc(rows, sizeof(double *));
    if (res->a == NULL)
	return 0;

    for (i = 0; i < rows; i++) {
	res->a[i] = (double *)G_calloc(cols, sizeof(double));
	if (res->a[i] == NULL) {
	    for (j = 0; j < i; j++)
		G_free(res->a[j]);
	    G_free(res->a);
	    return 0;
	}
    }

    return 1;
}

void matrix_free(MATRIX *m)
{
    int i;

    for (i = 0; i < m->rows; i++)
	G_free(m->a[i]);
    G_free(m->a);
    return;
}

int matrix_mult(MATRIX *a, MATRIX *b, MATRIX *res)
{
    if (a->cols != b->rows)
	return 0;

    /*if (!matrix_init(a.rows, b.cols, res))
     * return 0;
     */

    int i, j, k;

    for (i = 0; i < a->rows; i++)
	for (j = 0; j < b->cols; j++) {
	    res->a[i][j] = 0;
	    for (k = 0; k < a->cols; k++)
		res->a[i][j] += a->a[i][k] * b->a[k][j];
	}

    return 1;
}

int matrix_add_identity(double s, MATRIX *m)
{
    if (m->rows != m->cols)
	return 0;
    int i;

    for (i = 0; i < m->rows; i++)
	m->a[i][i] += s;

    return 1;
}

/* three following functions implements elementary row operations on matrices */

/* auxialiry function for matrix_inverse, swaps two rows of given matrix */
void matrix_swap_rows(int x, int y, MATRIX *m)
{
    int i;

    for (i = 0; i < m->cols; i++) {
	double t;

	t = m->a[x][i];
	m->a[x][i] = m->a[y][i];
	m->a[y][i] = t;
    }
    return;
}

/* auxiliary function for matrix_inverse, multiplies row of a matrix by
 * a scalar */
void matrix_row_scalar(int row, double s, MATRIX *m)
{
    int i;

    for (i = 0; i < m->cols; i++)
	m->a[row][i] *= s;
    return;
}

/* auxiliary function for matrix_inverse, adds a multiple of 
 * one row to another.
 * i.e row[ra] = row[ra] + row[rb] * s;
 */
void matrix_row_add_multiple(int ra, int rb, double s, MATRIX *m)
{
    int i;

    for (i = 0; i < m->cols; i++)
	m->a[ra][i] += m->a[rb][i] * s;
    return;
}

/* TODO: don't test directly equality to zero */
int matrix_inverse(MATRIX *a, MATRIX *res, int percents)
{
    int i, j;

    /* not a square matrix */
    if (a->rows != a->cols)
	return 0;

    /* initialize output matrix to the identity matrix */
    if (!matrix_init(a->rows, a->rows, res)) {
	G_fatal_error(_("Out of memory"));
	return 0;
    }
    for (i = 0; i < a->rows; i++) {
	memset(res->a[i], 0, sizeof(double) * a->cols);
	res->a[i][i] = 1;
    }

    /* in order to obtain the inverse of a matrix, we run
     * gauss elimination on the matrix and each time we apply
     * elementary row operation on this matrix, we apply the
     * same operation on the identity matrix. Correctness of
     * this follows from the fact that an invertible matrix
     * is row equivalent to the identity matrix.
     */

    int n = a->rows;

    if (percents)
	G_percent_reset();

    for (i = 0; i < n; i++) {
	int found = 0;
	double c;

	if (percents)
	    G_percent(i, n, 1);
	for (j = i; j < n; j++) {
	    if (a->a[j][i] != 0) {	/* need to change this row to something */
		found = 1;	/* more sensible */
		matrix_swap_rows(i, j, a);
		matrix_swap_rows(i, j, res);
		break;
	    }
	}
	if (!found)
	    return 0;

	c = (double)1.0 / a->a[i][i];

	matrix_row_scalar(i, c, a);
	matrix_row_scalar(i, c, res);
	for (j = 0; j < n; j++) {
	    if (i == j)
		continue;

	    c = -a->a[j][i];

	    if (c == 0.0)
		continue;
	    matrix_row_add_multiple(j, i, c, a);
	    matrix_row_add_multiple(j, i, c, res);
	}
    }

    return 1;
}

void matrix_mult_scalar(double s, MATRIX *m)
{
    int i, j;

    for (i = 0; i < m->rows; i++)
	for (j = 0; j < m->cols; j++)
	    m->a[i][j] *= s;
}

void matrix_add(MATRIX *a, MATRIX *b, MATRIX *res)
{
    int i, j;

    for (i = 0; i < res->rows; i++)
	for (j = 0; j < res->cols; j++)
	    res->a[i][j] = a->a[i][j] + b->a[i][j];
}

void matrix_print(MATRIX *a)
{
    int i, j;

    for (i = 0; i < a->rows; i++) {
	double s = 0;

	for (j = 0; j < a->cols; j++) {
	    printf("%.3lf ", a->a[i][j]);
	    s += a->a[i][j];
	}
	printf("|%.5lf\n", s);
    }
    printf("\n");

}
