aced/src/matrix.c

173 lines
4.7 KiB
C

#include "../includes/matrix.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
// Prints a debug view of a `matrix_t` to standard output.
void DebugPrintMatrix(matrix_t *matrix) {
printf("Grid ( len: %zu, head: %p ) { ", (matrix->len),
(void *)(matrix->head));
printf("data: [");
for (size_t i = 0; i < matrix->len; i++) {
if (i > 0) {
printf(", ");
} else {
printf(" ");
}
printf("%" PRImDATA, *(matrix->head + i));
}
printf("] }\n");
}
// Prints a row of a `matrix_t` to standard output.
void PrintMatrixRow(matrix_t *matrix, size_t row) {
for (size_t entry = 0; entry < matrix->row_len; entry++) {
printf("%" PRImDATA, *(matrix->head + row * (matrix->row_len) + entry));
if (entry != matrix->row_len - 1) {
printf(", ");
}
}
printf("\n");
}
// Parameters for `increase_input_buffers`.
typedef struct {
size_t capacity;
data_t *head;
} matrix_bufs_t;
// Increases the data buffers used in `parse_input`.
//
// As reallocation will happen, the updated pointers and capacity are returned
// in an updated parameters object.
static matrix_bufs_t increase_input_buffers(matrix_bufs_t bufs) {
bufs.capacity *= 2;
bufs.head = realloc(bufs.head, bufs.capacity * sizeof(data_t));
if (bufs.head == NULL) {
printf(
"Failed to reallocate bigger buffer for input data. Was trying to "
"allocate %zu entries, aborting.",
bufs.capacity);
exit(EXIT_FAILURE);
}
return bufs;
}
// Compacts the allocated memory to fit exactly the number of elements.
static void compact_buffers(matrix_t *matrix) {
matrix->head = realloc(matrix->head, matrix->len * sizeof(data_t));
if ((matrix->head == NULL)) {
fprintf(stderr,
"Failed to reallocate matrix data buffers. Was trying to "
"allocate for %zu data entries, aborting.",
matrix->len);
exit(EXIT_FAILURE);
}
}
static data_t as_matrix_entry(char *str, size_t str_len) {
data_t value;
str[str_len] = 0; // Null-terminate the string.
int result = sscanf(str, "%" SCNmDATA, &value);
if (result == 0 || result == EOF) {
// Failed to parse this as an entry.
fprintf(stderr, "Cannot parse %s as a numerical matrix entry, aborting.",
str);
exit(EXIT_FAILURE);
}
return value;
}
// Parse a `matrix_t` by reading from standard input.
//
// We expect whitespace separated (possibly signed) integer values.
matrix_t ParseMatrix(size_t row_len) {
size_t capacity = 32; // Initial value is arbitrary.
size_t len = 0; // Number of entries stored.
data_t *head = malloc(capacity * sizeof(data_t));
_Bool spaces = 1;
char scanned;
char scan_str[50];
size_t scan_len = 0;
while (scanf("%c", &scanned) != EOF) {
if (isspace(scanned)) {
if (!spaces) {
// Finished reading string.
// Push the numerical entry into the matrix.
data_t value = as_matrix_entry(scan_str, scan_len);
head[len - 1] = value;
}
spaces = 1;
} else {
// A little input sanitization.
if (!(isdigit(scanned) || (scanned == '-'))) {
fprintf(
stderr,
"Foreign character %c (%x) found when processing input, aborting.",
scanned, scanned);
exit(EXIT_FAILURE);
}
if (spaces) {
// New string
len++;
scan_len = 0;
// Check if we need to resize the buffer.
if (len > capacity) {
matrix_bufs_t bufs = {
.capacity = capacity,
.head = head,
};
bufs = increase_input_buffers(bufs);
capacity = bufs.capacity;
head = bufs.head;
}
}
spaces = 0;
// Save the character to the entry string
scan_str[scan_len] = scanned;
scan_len++;
if (scan_len == 50) {
fprintf(stderr,
"An entry in the matrix is too long (over 50 characters).");
exit(EXIT_FAILURE);
}
}
} // End of scan loop
// Write the last entry if necessary
if (!spaces) {
data_t value = as_matrix_entry(scan_str, scan_len);
head[len - 1] = value;
}
// Input sanitization: confirm that the number of entries read is a multiple
// of the number of entries in a row.
if (!((len % row_len) == 0)) {
fprintf(stderr,
"Number of entries is not consistent with provided row length. "
"Got row length of %zu, and read %zu entries. Aborting.",
row_len, len);
// No need to free the buffers.
exit(EXIT_FAILURE);
}
matrix_t matrix = {
.row_len = row_len,
.head = head,
.len = len,
};
compact_buffers(&matrix);
return matrix;
}
void MatrixFree(matrix_t *matrix) {
free(matrix->head);
}