Follow up on this post. I have refactored my code and created a matrix_t struct and implemented various functions to create, delete, modify and perform arithmetic on them.
matrix.h
#ifndef MATRIX_H
#define MATRIX_H
typedef struct matrix_t
{
size_t rows;
size_t cols;
double *data;
} matrix_t;
matrix_t *mx_new(const size_t rows, const size_t cols);
matrix_t *mx_identity(const size_t size);
matrix_t *mx_ones(const size_t rows, const size_t cols);
matrix_t *mx_clone(const matrix_t *matrix);
matrix_t *mx_row_major(const double *data, const size_t rows, const size_t cols);
matrix_t *mx_col_major(const double *data, const size_t rows, const size_t cols);
void mx_free(matrix_t *matrix);
void mx_print(const matrix_t *matrix);
void mx_fprint(const matrix_t *matrix, FILE *stream);
void mx_set(const matrix_t *matrix, const size_t i, const size_t j, const double x);
double mx_get(const matrix_t *matrix, const size_t i, const size_t j);
void mx_set_row(const matrix_t *matrix, const size_t i, const double *elem);
void mx_set_col(const matrix_t *matrix, const size_t j, const double *elem);
double *mx_get_row(const matrix_t *matrix, const size_t i);
double *mx_get_col(const matrix_t *matrix, const size_t j);
matrix_t *mx_add(const matrix_t *matrixA, const matrix_t *matrixB);
matrix_t *mx_sub(const matrix_t *matrixA, const matrix_t *matrixB);
matrix_t *mx_mul(const matrix_t *matrixA, const matrix_t *matrixB);
matrix_t *mx_elem_mul(const matrix_t *matrixA, const matrix_t *matrixB);
matrix_t *mx_elem_div(const matrix_t *matrixA, const matrix_t *matrixB);
matrix_t *mx_scalar_mul(const matrix_t *matrix, const double x);
matrix_t *mx_scalar_div(const matrix_t *matrix, const double x);
matrix_t *mx_transpose(const matrix_t *matrix);
void mx_swap_rows(matrix_t *matrix, const size_t i1, const size_t i2);
void mx_swap_cols(matrix_t *matrix, const size_t j1, const size_t j2);
#endif
matrix.c
#include <stdio.h>
#include <stdlib.h>
#include "matrix.h"
#ifndef OUT_OF_MEMORY
#define OUT_OF_MEMORY "Insufficient space in memory. Program terminated.\n"
#endif
/* Matrix Memory Management Functions */
matrix_t *mx_new(const size_t rows, const size_t cols)
{
matrix_t *matrix = malloc(sizeof(matrix_t));
if (!matrix) {
fprintf(stderr, OUT_OF_MEMORY);
}
matrix->rows = rows;
matrix->cols = cols;
matrix->data = malloc(rows * cols * sizeof(double));
if (!matrix->data) {
fprintf(stderr, OUT_OF_MEMORY);
}
return matrix;
}
matrix_t *mx_identity(const size_t size)
{
matrix_t *matrix = mx_new(size, size);
for (size_t i = 0; i < size; i++)
{
for (size_t j = 0; j < size; j++)
{
mx_set(matrix, i, j, (i == j ? 1 : 0));
}
}
return matrix;
}
matrix_t *mx_ones(const size_t rows, const size_t cols)
{
matrix_t *matrix = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(matrix, i, j, 1);
}
}
return matrix;
}
matrix_t *mx_row_major(const double *data, const size_t rows, const size_t cols) {
matrix_t *matrix = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(matrix, i, j, *(data + i * rows + j));
}
}
return matrix;
}
matrix_t *mx_col_major(const double *data, const size_t rows, const size_t cols) {
matrix_t *matrix = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(matrix, i, j, *(data + j * cols + i));
}
}
return matrix;
}
matrix_t *mx_clone(const matrix_t *matrix)
{
size_t rows, cols;
matrix_t *copy;
rows = matrix->rows;
cols = matrix->cols;
copy = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(copy, i, j, mx_get(matrix, i, j));
}
}
return copy;
}
void mx_free(matrix_t *matrix)
{
free(matrix->data);
free(matrix);
}
/* Matrix Output Functions */
void mx_fprint(const matrix_t *matrix, FILE *stream)
{
for (size_t i = 0; i < matrix->rows; i++)
{
for (size_t j = 0; j < matrix->cols; j++)
{
fprintf(stream, "%5.2f ", mx_get(matrix, i, j));
}
fprintf(stream, "\n");
}
}
void mx_print(const matrix_t *matrix)
{
mx_fprint(matrix, stdout);
}
/* Setting and Getting individual elements as well as rows and columns*/
void mx_set(const matrix_t *matrix, const size_t i, const size_t j, const double x)
{
*(matrix->data + i * matrix->rows + j) = x;
}
double mx_get(const matrix_t *matrix, const size_t i, const size_t j)
{
return *(matrix->data + i * matrix->rows + j);
}
void mx_set_row(const matrix_t *matrix, const size_t i, const double *elem)
{
for (size_t j = 0; j < matrix->cols; j++)
{
mx_set(matrix, i, j, *(elem + j));
}
}
void mx_set_col(const matrix_t *matrix, const size_t j, const double *elem)
{
for (size_t i = 0; i < matrix->rows; i++)
{
mx_set(matrix, i, j, *(elem + i));
}
}
double *mx_get_row(const matrix_t *matrix, const size_t i)
{
double *elem = malloc(matrix->cols *sizeof(double));
if (!elem)
{
fprintf(stderr, OUT_OF_MEMORY);
exit(EXIT_FAILURE);
}
for (size_t j = 0; j < matrix->cols; j++)
{
*(elem + j) = mx_get(matrix, i, j);
}
return elem;
}
double *mx_get_col(const matrix_t *matrix, const size_t j)
{
double *elem = malloc(matrix->rows *sizeof(double));
if (!elem)
{
fprintf(stderr, OUT_OF_MEMORY);
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < matrix->rows; i++)
{
*(elem + i) = mx_get(matrix, i, j);
}
return elem;
}
/* Arithmetic on Matrices */
static void check_same_size(const matrix_t *matrixA, const matrix_t *matrixB)
{
if (matrixA->rows == matrixB->rows && matrixA->cols == matrixB->cols)
{
return;
}
fprintf(stderr, "Matrices are not compatible. They have different dimensions.\n");
exit(EXIT_FAILURE);
}
matrix_t *mx_add(const matrix_t *matrixA, const matrix_t *matrixB)
{
check_same_size(matrixA, matrixB);
size_t rows = matrixA->rows;
size_t cols = matrixA->cols;
matrix_t *sum = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
double a = mx_get(matrixA, i, j);
double b = mx_get(matrixB, i, j);
mx_set(sum, i, j, a + b);
}
}
return sum;
}
matrix_t *mx_sub(const matrix_t *matrixA, const matrix_t *matrixB)
{
check_same_size(matrixA, matrixB);
size_t rows = matrixA->rows;
size_t cols = matrixA->cols;
matrix_t *diff = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
double a = mx_get(matrixA, i, j);
double b = mx_get(matrixB, i, j);
mx_set(diff, i, j, a - b);
}
}
return diff;
}
matrix_t *mx_mul(const matrix_t *matrixA, const matrix_t *matrixB)
{
// check the size
if (matrixA->cols != matrixB->rows)
{
fprintf(stderr, "Matrices are not compatible for multiplication.\n");
exit(EXIT_FAILURE);
}
size_t m = matrixA->rows;
size_t n = matrixA->cols;
size_t p = matrixB->cols;
matrix_t *product = mx_new(m, p);
// standard general multiplication
for (size_t i = 0; i < m; i++)
{
for (size_t j = 0; j < p; j++)
{
double sum = 0;
for (size_t k = 0; k < n; k++)
{
sum += mx_get(matrixA, i, k) * mx_get(matrixB, k, j);
}
mx_set(product, i, j, sum);
}
}
return product;
}
matrix_t *mx_elem_mul(const matrix_t *matrixA, const matrix_t *matrixB)
{
check_same_size(matrixA, matrixB);
size_t rows = matrixA->rows;
size_t cols = matrixA->cols;
matrix_t *result = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
double a = mx_get(matrixA, i, j);
double b = mx_get(matrixB, i, j);
mx_set(result, i, j, a * b);
}
}
return result;
}
matrix_t *mx_elem_div(const matrix_t *matrixA, const matrix_t *matrixB)
{
check_same_size(matrixA, matrixB);
size_t rows = matrixA->rows;
size_t cols = matrixA->cols;
matrix_t *result = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
double a = mx_get(matrixA, i, j);
double b = mx_get(matrixB, i, j);
mx_set(result, i, j, a / b);
}
}
return result;
}
matrix_t *mx_scalar_mul(const matrix_t *matrix, const double x)
{
size_t rows = matrix->rows;
size_t cols = matrix->cols;
matrix_t *result = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(result, i, j, mx_get(matrix, i, j) * x);
}
}
return result;
}
matrix_t *mx_scalar_div(const matrix_t *matrix, const double x)
{
size_t rows = matrix->rows;
size_t cols = matrix->cols;
matrix_t *result = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(result, i, j, mx_get(matrix, i, j) / x);
}
}
return result;
}
/* Special Matrix Functions */
matrix_t *mx_transpose(const matrix_t *matrix)
{
size_t rows = matrix->cols;
size_t cols = matrix->rows;
matrix_t *trans = mx_new(rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
{
mx_set(trans, i, j, mx_get(matrix, i, j));
}
}
}
/* Matrix Mutating Functions */
void mx_swap_rows(matrix_t *matrix, const size_t i1, const size_t i2)
{
double *row = mx_get_row(matrix, i1);
mx_set_row(matrix, i1, mx_get_row(matrix, i2));
mx_set_row(matrix, i2, row);
}
void mx_swap_cols(matrix_t *matrix, const size_t j1, const size_t j2)
{
double *col = mx_get_col(matrix, j1);
mx_set_col(matrix, j1, mx_get_col(matrix, j2));
mx_set_col(matrix, j2, col);
}
I'd like comments and critique on general code style, performance issues, and anything that seems out of place.