/* -*- c -*-
 * Time-stamp: <2005-04-16 11:28:45 rsmith>
 * 
 * matrix/matrix.c
 * Copyright (C) 2002,2004,2005 R.F. Smith <rsmith@xs4all.nl>. 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* If this code fails to compile, check that you compiler supports
 * variable-length automatic arrays. If compilation fails with GCC, try
 * adding the -fno-unit-at-a-time switch to the compilation command. */

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "matrix.h"

#ifndef NULL
#define NULL (void*)0
#endif

#ifndef RETURN_IF_FAIL
#define RETURN_IF_FAIL(test) if (!(test)) return
#endif

#ifndef RETURN_VAL_IF_FAIL
#define RETURN_VAL_IF_FAIL(test,val) if (!(test)) return (val)
#endif

#ifndef NDEBUG
/* __FUNCTION__ and varags macros are a GCC feature. */
#  ifdef __GNUC__
#    undef debug
#    define debug(a...) fprintf(stderr,"%s, line %i, %s(): ",\
__FILE__,__LINE__,__FUNCTION__); fprintf(stderr, ## a); fprintf(stderr, "\n")
#  else
#    undef debug
#    define debug(a) (void)0
#  endif                        /* __GNUC__ */
#else
#  undef debug
#  define debug(a) (void)0
#endif                          /* NDEBUG */

/* This enables you to see which files have been used in building a
 * binary. N.B, __attribute is a GCC extension, so we test for __GNUC__. */
#ifdef __GNUC__
static char *SCMID __attribute__ ((unused)) = 
"$Id: matrix.c 33 2005-04-16 21:47:48Z rsmith $";
#endif

/* If the product of the multiplication of two doubles is less than EPS,
 * the value is set to 0. */
#define EPS 1e-8

int mat_unity(int n, double a[n][n]);
int mat_cpy(int n, double a[n][n], double b[n][n]);
int mat_xform(int n, double m[n][n], double v[n], double mv[n]);
int mat_mul(int n, double a[n][n], double b[n][n], double ab[n][n]);
int mat_inv(int n, double m[n][n], double i[n][n]);
int mat_print(char *name, int n, double m[n][n]);

int mat_unity(int n, double a[n][n])
{
	int r, c;
	assert(n>0);
	RETURN_VAL_IF_FAIL(n>0,1);
	for (r=0; r<n; r++) {
		for (c=0; c<n; c++) {
			if (r==c) {
				a[r][c] = 1.0;
			} else {
				a[r][c] = 0.0;
			}
		}
	}
	return 0;
}

int mat_cpy(int n, double a[n][n], double b[n][n])
{
	int r, c;
	assert(n>0);
	RETURN_VAL_IF_FAIL(n>0,1);
	/* Make a copy of m */
	for (r=0; r<n; r++) {
		for (c=0; c<n; c++) {
			b[r][c] = a[r][c];
		}
	}
	return 0;
}

int mat_xform(int n, double m[n][n], double v[n], double mv[n])
{
	int r, c;
	double res[n];
	assert(n>0);
	RETURN_VAL_IF_FAIL(n>0,1);
	for (r=0; r<n; r++) {
		res[r] = 0.0;
		for (c=0; c<n; c++) {
			res[r] += m[r][c]*v[c];
		}
	}
	memcpy(mv, res, n*sizeof(double));
	return 0;
}

int mat_mul(int n, double a[n][n], double b[n][n], double ab[n][n])
{
	int r, c, t;
	double res[n][n];
	assert(n>0);
	RETURN_VAL_IF_FAIL(n>0,1);
	for (r=0; r<n; r++) {
		for (c=0; c<n; c++) {
			res[r][c] = 0.0;
			for (t=0; t<n; t++) {
				res[r][c] += a[r][t]*b[t][c];
			}
			if (fabs(res[r][c]) < EPS) {
				res[r][c] = 0.0;
			}
		}
	}
	memcpy(ab, res, n*n*sizeof(double));
	return 0;
}

int mat_inv(int n, double m[n][n], double i[n][n])
{
	int r, c, t;
	double m2[n][n], inv[n][n], factor;
	assert(n>0);
	RETURN_VAL_IF_FAIL(n>0,1);
	mat_cpy(n, m, m2);
	mat_unity(n, inv);
	/* Empty the lower half of the matrix */
	for (t=0; t<n-1; t++) {
		if (m2[t][t] == 0) {
			return 2;
		}
		for (r=t+1; r<n; r++) {
			factor=m2[r][t]/m2[t][t];
			for (c=0; c<n; c++) {
				inv[r][c] -= factor*inv[t][c];
				if (c<=t) {
					m2[r][c]=0;
				} else {
					m2[r][c] -= factor*m2[t][c];
				}
			}
		}
	}
	/* Now empty the rest. */
	for (t=n-1; t>0; t--) {
		for (r=t-1; r>=0; r--) {
			factor = m2[r][t]/m2[t][t];
			for (c=n-1; c>=0; c--) {
				if (c==t) {
					m2[r][c] = 0;
				}
				inv[r][c]-=factor*inv[t][c];
			}
		}
	}
	/* Correct the rows' magnitude */
	for (r=0; r<n; r++) {
		for (c=0; c<n; c++) {
			inv[r][c] /= m2[r][r];
		}
		m2[r][r] = 1.0;
	}
	mat_cpy(n, inv, i);
	return 0;
}

int mat_print(char *name, int n, double m[n][n])
{
	int r, c;
	assert(n>0);
	assert(name!=NULL);
	RETURN_VAL_IF_FAIL(n>0,1);
	RETURN_VAL_IF_FAIL(name!=NULL,2);
	printf("matrix %s =\n", name);
	for (r=0; r<n; r++) {
		for (c=0; c<n; c++) {
			if (c==0) {
				printf("|%g ", m[r][c]);
			} else if (c==n-1) {
				printf("%g|\n", m[r][c]);
			} else {
				printf("%g ", m[r][c]);
			}
		}
	}
	return 0;
}
/* EOF matrix.c */


syntax highlighted by Code2HTML, v. 0.9.1