Compare commits

...

4 Commits

Author SHA1 Message Date
Miguel M 1bf3c6a19d Radical simplification of matrix_t
Treating the matrix entries as strings only served to introduce a level
of indirection which wasn't needed, since all of the given real-world
data has simple (very small) signed integer values. Now I just parse
those into a contiguous region of memory.
2023-04-26 17:09:07 +01:00
Miguel M f299b689f2 misc. corrections 2023-04-26 15:48:11 +01:00
Miguel M c5f5a5f1ff Function to write out a row 2023-04-26 15:47:44 +01:00
Miguel M 5b41def60a grid_data_t->matrix_data_t, for consistency 2023-04-26 15:25:56 +01:00
3 changed files with 109 additions and 119 deletions

View File

@ -4,36 +4,23 @@
#include <stddef.h>
#include <inttypes.h>
#define PRIgridDATA PRIiFAST32
typedef int_fast32_t grid_data_t;
#define PRImDATA PRIiFAST32
#define SCNmDATA SCNiFAST32
typedef int_fast32_t matrix_data_t;
// Struct representing the input matrix.
typedef struct
{
// Number of elements in one row.
size_t row_len;
// Pointer to the buffer containing the matrix's characters.
// The data is organized as <string length> <chars...>, such that the `char`s
// are casted to `int_fast32_t` to allow for the mixed types.
grid_data_t *data_head;
// Array of indices in `data_head` of the start of each string.
size_t *entries_head;
// Number of bytes.
size_t data_len;
// Number of entries.
size_t entries_len;
// Pointer to the buffer containing the matrix's entries.
matrix_data_t *head;
// Number of entries in the matrix.
size_t len;
} matrix_t;
// Parameters for `increase_input_buffers`.
typedef struct
{
size_t data_cap;
grid_data_t *data_head;
size_t *entries_head;
} matrix_bufs_t;
// Prints a debug view of a `matrix_t` to standard output.
void DebugPrintGrid(matrix_t *grid);
void DebugPrintMatrix(matrix_t *matrix);
// Parse a matrix from the standard input.
//

View File

@ -1,6 +1,6 @@
#include <stdio.h>
#include "../includes/matrix.h"
// Temporarily defined here.
// Later, should be defined upon compilation, with -D flag (for gcc).
#define A_OUTPUTS 3
@ -8,6 +8,16 @@
#define A_INPUTS 3
#define B_INPUTS 3
void PrintRow(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");
}
int main(int argc, char *argv[])
{
size_t a_out = A_OUTPUTS;
@ -15,7 +25,15 @@ int main(int argc, char *argv[])
size_t a_in = A_INPUTS;
size_t b_in = B_INPUTS;
size_t row_len = ((a_out-1)*(b_out-1)*a_in*b_in + (a_out-1)*a_in +
(b_out-1)*b_in);
(b_out-1)*b_in) + 1; // +1 accounts for the L column.
matrix_t matrix = ParseMatrix(row_len);
DebugPrintMatrix(&matrix);
size_t row_count = matrix.len / row_len;
for (size_t row = 0; row < row_count; row++) {
// Compare to the following rows
PrintRow(&matrix, row);
}
return 0;
}

View File

@ -4,14 +4,12 @@
#include <ctype.h>
// Prints a debug view of a `matrix_t` to standard output.
void DebugPrintGrid(matrix_t *matrix)
void DebugPrintMatrix(matrix_t *matrix)
{
printf("Grid ( row_len: %zu, data_head: %p, entries_head: %p, data_len: %zu, entries_len: %zu ) { ",
(matrix->row_len), (void *)(matrix->data_head), (void *)(matrix->entries_head),
(matrix->data_len), (matrix->entries_len));
printf("Grid ( len: %zu, head: %p ) { ",
(matrix->len), (void *)(matrix->head));
printf("data: [");
size_t str_counter = 0;
for (size_t i = 0; i < matrix->data_len; i++)
for (size_t i = 0; i < matrix->len; i++)
{
if (i > 0)
{
@ -21,109 +19,95 @@ void DebugPrintGrid(matrix_t *matrix)
{
printf(" ");
}
_Bool is_char = 1;
if (matrix->entries_len > 0 && str_counter < matrix->entries_len)
{
if ((matrix->entries_head)[str_counter] == i)
{
is_char = 0;
str_counter += 1;
}
}
if (is_char)
{
printf("'%c'", (char)((matrix->data_head)[i]));
}
else
{
printf("%" PRIgridDATA, (matrix->data_head)[i]);
}
}
printf("], entries: [");
for (size_t i = 0; i < matrix->entries_len; i++)
{
if (i > 0)
{
printf(", ");
}
else
{
printf(" ");
}
printf("%zu", (matrix->entries_head)[i]);
printf("%" PRImDATA, *(matrix->head + i));
}
printf("] }\n");
}
// Parameters for `increase_input_buffers`.
typedef struct
{
size_t capacity;
matrix_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.
matrix_bufs_t increase_input_buffers(matrix_bufs_t bufs)
static matrix_bufs_t increase_input_buffers(matrix_bufs_t bufs)
{
bufs.data_cap *= 2;
bufs.data_head = realloc(bufs.data_head, bufs.data_cap * sizeof(grid_data_t));
bufs.entries_head = realloc(bufs.entries_head, bufs.data_cap * sizeof(grid_data_t *));
if ((bufs.data_head == NULL) || (bufs.entries_head == NULL))
bufs.capacity *= 2;
bufs.head = realloc(bufs.head, bufs.capacity * sizeof(matrix_data_t));
if (bufs.head == NULL)
{
printf(
"Failed to reallocate bigger buffer for input data. Was trying to "
"allocate %zu entries, aborting.",
bufs.data_cap);
bufs.capacity);
exit(EXIT_FAILURE);
}
return bufs;
}
// Compacts the allocated memory to fit exactly the number of elements.
//
// As reallocation will happen, the updated pointers and capacity are returned
// in an updated parameters object.
matrix_bufs_t compact_buffers(matrix_t *matrix) {
matrix->data_head = realloc(matrix->data_head, matrix->data_len * sizeof(grid_data_t));
matrix->entries_head = realloc(matrix->entries_head, matrix->entries_len * sizeof(grid_data_t *));
if ((matrix->data_head == NULL) || (matrix->entries_head == NULL))
static void compact_buffers(matrix_t *matrix)
{
matrix->head = realloc(matrix->head, matrix->len * sizeof(matrix_data_t));
if ((matrix->head == NULL))
{
fprintf(
stderr,
"Failed to reallocate matrix data buffers. Was trying to "
"allocate for %zu data entries, aborting.",
matrix->data_len);
matrix->len);
exit(EXIT_FAILURE);
}
}
static matrix_data_t as_matrix_entry(char *str, size_t str_len) {
matrix_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.
//
// Parsing the entries of the matrix as numeric is unnecessary, since we just
// want to check equality of rows under certain permutations. Therefore, the
// matrix is stored as literal characters.
//
// To make data access O(1), we store both the literal characters, as well as
// indices to the start of each entry. See the `Grid` struct. In particular,
// what we have is contiguous blocks of strings, structured as
//
// <string length> <characters...>
// We expect whitespace separated (possibly signed) integer values.
matrix_t ParseMatrix(size_t row_len)
{
size_t data_cap = 32; // Initial value is arbitrary.
size_t data_len = 0; // Number of characters written so far.
size_t entries_len = 0; // Number of strings written so far.
grid_data_t *data_head = malloc(data_cap * sizeof(grid_data_t));
size_t *entries_head = malloc(data_cap * sizeof(size_t));
size_t capacity = 32; // Initial value is arbitrary.
size_t len = 0; // Number of entries stored.
matrix_data_t *head = malloc(capacity * sizeof(matrix_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.
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 == '-') || (scanned == '.')))
if (!(isdigit(scanned) || (scanned == '-')))
{
fprintf(
stderr,
@ -135,56 +119,57 @@ matrix_t ParseMatrix(size_t row_len)
if (spaces)
{
// New string
entries_len += 1;
data_len += 1; // The byte for the string length
// data_head[data_len] is already correctly initialized to 0.
entries_head[entries_len - 1] = data_len - 1;
len += 1;
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;
// We're about to write a new character.
data_len += 1;
// Save the character to the entry string
scan_str[scan_len] = scanned;
scan_len += 1;
// Check if we need to resize the buffer.
if (data_len > data_cap)
if (scan_len == 50)
{
matrix_bufs_t bufs = {
.data_cap = data_cap,
.data_head = data_head,
.entries_head = entries_head,
};
bufs = increase_input_buffers(bufs);
data_cap = bufs.data_cap;
data_head = bufs.data_head;
entries_head = bufs.entries_head;
fprintf(stderr, "An entry in the matrix is too long (over 50 characters).");
exit(EXIT_FAILURE);
}
// Write the character
data_head[data_len - 1] = scanned;
// Increase the length of current string
data_head[entries_head[entries_len - 1]] += 1;
}
} // End of scan loop
// Write the last entry if necessary
if (!spaces) {
matrix_data_t value = as_matrix_entry(scan_str, scan_len);
head[len] = value;
}
// Input sanitization: confirm that the number of entries read is a multiple
// of the number of entries in a row.
if (!((entries_len % row_len) == 0))
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, entries_len);
"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,
.data_head = data_head,
.entries_head = entries_head,
.data_len = data_len,
.entries_len = entries_len,
.head = head,
.len = len,
};
compact_buffers(&matrix);