aboutsummaryrefslogtreecommitdiff
path: root/libcrystfel/src/integer_matrix.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcrystfel/src/integer_matrix.c')
-rw-r--r--libcrystfel/src/integer_matrix.c386
1 files changed, 386 insertions, 0 deletions
diff --git a/libcrystfel/src/integer_matrix.c b/libcrystfel/src/integer_matrix.c
new file mode 100644
index 00000000..5aefb958
--- /dev/null
+++ b/libcrystfel/src/integer_matrix.c
@@ -0,0 +1,386 @@
+/*
+ * integer_matrix.c
+ *
+ * A small integer matrix library
+ *
+ * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2012 Thomas White <taw@physics.org>
+ *
+ * This file is part of CrystFEL.
+ *
+ * CrystFEL is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * CrystFEL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "integer_matrix.h"
+
+
+/**
+ * SECTION:integer_matrix
+ * @short_description: Integer matrices
+ * @title: Integer matrices
+ * @section_id:
+ * @see_also:
+ * @include: "integer_matrix.h"
+ * @Image:
+ *
+ * Routines to handle integer matrix
+ */
+
+
+struct _integermatrix
+{
+ unsigned int rows;
+ unsigned int cols;
+
+ signed int *v;
+};
+
+
+/**
+ * intmat_new:
+ *
+ * Allocates a new %IntegerMatrix with all elements set to zero.
+ *
+ * Returns: a new %IntegerMatrix, or NULL on error.
+ **/
+IntegerMatrix *intmat_new(unsigned int rows, unsigned int cols)
+{
+ IntegerMatrix *m;
+
+ m = malloc(sizeof(IntegerMatrix));
+ if ( m == NULL ) return NULL;
+
+ m->v = calloc(rows*cols, sizeof(signed int));
+ if ( m->v == NULL ) {
+ free(m);
+ return NULL;
+ }
+
+ m->rows = rows;
+ m->cols = cols;
+
+ return m;
+}
+
+
+/**
+ * intmat_free:
+ * @m: An %IntegerMatrix
+ *
+ * Frees @m.
+ **/
+void intmat_free(IntegerMatrix *m)
+{
+ free(m->v);
+ free(m);
+}
+
+
+/**
+ * intmat_set:
+ * @m: An %IntegerMatrix
+ * @i: row number to set
+ * @j: column number to set
+ * @v: value to set to
+ *
+ * Sets the @i,@j element of @m to @v.
+ **/
+void intmat_set(IntegerMatrix *m, unsigned int i, unsigned int j, signed int v)
+{
+ assert(i < m->rows);
+ assert(j < m->cols);
+ m->v[j + m->cols*i] = v;
+}
+
+
+/**
+ * intmat_get:
+ * @m: An %IntegerMatrix
+ * @i: column number to set
+ * @j: row number to set
+ *
+ * Gets the @i,@j element of @m.
+ *
+ * Returns: the @i,@j element of @m.
+ **/
+signed int intmat_get(IntegerMatrix *m, unsigned int i, unsigned int j)
+{
+ assert(i < m->rows);
+ assert(j < m->cols);
+ return m->v[j + m->cols*i];
+}
+
+
+/**
+ * intmat_intvec_mult:
+ * @m: An %IntegerMatrix
+ * @vec: An array of signed integers
+ *
+ * Multiplies the matrix @m by the vector @vec. The size of @vec must equal the
+ * number of columns in @m, and the size of the result equals the number of rows
+ * in @m.
+ *
+ * Returns: a newly allocated array of signed integers containing the answer,
+ * or NULL on error.
+ **/
+signed int *intmat_intvec_mult(IntegerMatrix *m, signed int *vec)
+{
+ signed int *ans;
+ unsigned int i;
+
+ ans = malloc(m->rows * sizeof(signed int));
+ if ( ans == NULL ) return NULL;
+
+ for ( i=0; i<m->rows; i++ ) {
+
+ unsigned int j;
+
+ ans[i] = 0;
+ for ( j=0; j<m->cols; j++ ) {
+ ans[i] += intmat_get(m, i, j) * vec[j];
+ }
+
+ }
+
+ return ans;
+}
+
+
+/**
+ * intmat_intmat_mult:
+ * @a: An %IntegerMatrix
+ * @b: An %IntegerMatrix
+ *
+ * Multiplies the matrix @a by the matrix @b.
+ *
+ * Returns: a newly allocated %IntegerMatrix containing the answer, or NULL on
+ * error.
+ **/
+IntegerMatrix *intmat_intmat_mult(IntegerMatrix *a, IntegerMatrix *b)
+{
+ unsigned int i, j;
+ IntegerMatrix *ans;
+
+ if ( a->cols != b->rows ) return NULL;
+
+ ans = intmat_new(a->rows, a->cols);
+ if ( ans == NULL ) return NULL;
+
+ for ( i=0; i<ans->rows; i++ ) {
+ for ( j=0; j<ans->cols; j++ ) {
+
+ unsigned int k;
+ signed int r = 0;
+
+ for ( k=0; k<a->cols; k++ ) { /* a->cols == b->rows */
+ r += intmat_get(a, i, k) * intmat_get(b, k, j);
+ }
+ intmat_set(ans, i, j, r);
+
+ }
+ }
+
+ return ans;
+}
+
+
+static IntegerMatrix *delete_row_and_column(IntegerMatrix *m,
+ unsigned int di, unsigned int dj)
+{
+ IntegerMatrix *n;
+ unsigned int i, j;
+
+ n = intmat_new(m->rows-1, m->cols-1);
+ if ( n == NULL ) return NULL;
+
+ for ( i=0; i<n->rows; i++ ) {
+ for ( j=0; j<n->cols; j++ ) {
+
+ signed int val;
+ unsigned int gi, gj;
+
+ gi = (i>=di) ? i+1 : i;
+ gj = (j>=dj) ? j+1 : j;
+ val = intmat_get(m, gi, gj);
+ intmat_set(n, i, j, val);
+
+ }
+ }
+
+ return n;
+}
+
+
+/**
+ * intmat_det:
+ * @m: An %IntegerMatrix
+ *
+ * Calculates the determinant of @m. Inefficiently.
+ *
+ * Returns: the determinant of @m.
+ **/
+signed int intmat_det(IntegerMatrix *m)
+{
+ unsigned int i, j;
+ signed int det = 0;
+
+ assert(m->rows == m->cols); /* Otherwise determinant doesn't exist */
+
+ if ( m->rows == 2 ) {
+ return intmat_get(m, 0, 0)*intmat_get(m, 1, 1)
+ - intmat_get(m, 0, 1)*intmat_get(m, 1, 0);
+ }
+
+ i = 0; /* Fixed */
+ for ( j=0; j<m->cols; j++ ) {
+
+ signed int C;
+ IntegerMatrix *n;
+ signed int t;
+
+ n = delete_row_and_column(m, i, j);
+ if ( n == NULL ) {
+ fprintf(stderr, "Failed to allocate matrix.\n");
+ return 0;
+ }
+
+ /* -1 if odd, +1 if even */
+ t = (i+j) & 0x1 ? -1 : +1;
+
+ C = t * intmat_det(n);
+ intmat_free(n);
+
+ det += intmat_get(m, i, j) * C;
+
+ }
+
+ return det;
+}
+
+
+static IntegerMatrix *intmat_cofactors(IntegerMatrix *m)
+{
+ IntegerMatrix *n;
+ signed int i, j;
+
+ n = intmat_new(m->rows, m->cols);
+ if ( n == NULL ) return NULL;
+
+ for ( i=0; i<n->rows; i++ ) {
+ for ( j=0; j<n->cols; j++ ) {
+
+ signed int C;
+ IntegerMatrix *M;
+ signed int t;
+
+ M = delete_row_and_column(m, i, j);
+ if ( M == NULL ) {
+ fprintf(stderr, "Failed to allocate matrix.\n");
+ return 0;
+ }
+
+ /* -1 if odd, +1 if even */
+ t = (i+j) & 0x1 ? -1 : +1;
+
+ C = t * intmat_det(M);
+ intmat_free(M);
+
+ intmat_set(n, i, j, C);
+
+ }
+ }
+
+ return n;
+}
+
+
+/**
+ * intmat_inverse:
+ * @m: An %IntegerMatrix
+ *
+ * Calculates the inverse of @m. Inefficiently.
+ *
+ * Returns: the inverse of @m, or NULL on error.
+ **/
+IntegerMatrix *intmat_inverse(IntegerMatrix *m)
+{
+ IntegerMatrix *adjugateT;
+ IntegerMatrix *inverse;
+ unsigned int i, j;
+ signed int det;
+
+ det = intmat_det(m);
+ if ( (det != +1) && (det != -1) ) {
+ fprintf(stderr, "Inverse matrix not an integer matrix.\n");
+ return NULL;
+ }
+
+ adjugateT = intmat_cofactors(m);
+ if ( adjugateT == NULL ) return NULL;
+
+ inverse = intmat_new(m->cols, m->rows); /* The other way round */
+ if ( inverse == NULL ) return NULL;
+
+ for ( i=0; i<inverse->rows; i++ ) {
+ for ( j=0; j<inverse->cols; j++ ) {
+
+ signed int v;
+
+ v = intmat_get(adjugateT, j, i);
+
+ /* 1/-1 = -1 and 1/+1 = +1, and these are the only two cases */
+ intmat_set(inverse, i, j, v*det);
+
+ }
+ }
+
+ intmat_free(adjugateT);
+
+ return inverse;
+}
+
+
+/**
+ * intmat_print
+ * @m: An %IntegerMatrix
+ *
+ * Prints @m to stderr.
+ *
+ */
+void intmat_print(IntegerMatrix *m)
+{
+ unsigned int i, j;
+
+ for ( i=0; i<m->rows; i++ ) {
+
+ fprintf(stderr, "[ ");
+ for ( j=0; j<m->cols; j++ ) {
+ fprintf(stderr, "%4i ", intmat_get(m, i, j));
+ }
+ fprintf(stderr, "]\n");
+ }
+}