Factored matrix (formerly grid) into a header

Includes also the memory compacting after all the input is read.
This commit is contained in:
Miguel M 2023-04-22 19:42:58 +01:00
parent 28058cdac3
commit eebca1914d
3 changed files with 241 additions and 203 deletions

46
includes/matrix.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef __H_MATRIX
#define __H_MATRIX
#include <stddef.h>
#include <inttypes.h>
#define PRIgridDATA PRIiFAST32
typedef int_fast32_t grid_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;
} 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 debug_print_grid(matrix_t *grid);
// Parse a matrix from the standard input.
//
// This function parses an extended matrix (C|l), where C is an n×m matrix, and
// l is an m-sized column vector. This input is given in standard input as
//
// <row length> <whitespace separated entries>
matrix_t parse_matrix(void);
#endif // __H_MATRIX

View File

@ -1,213 +1,12 @@
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
#include <ctype.h>
#include "../includes/matrix.h"
// #define STB_DS_IMPLEMENTATION
// #define STBDS_NO_SHORT_NAMES
// #include "../contrib/stb/stb_ds.h"
#define PRIgridDATA PRIiFAST32
typedef int_fast32_t grid_data_t;
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;
} grid_t;
// Parameters for `increase_input_buffers`.
typedef struct
{
size_t data_cap;
grid_data_t *data_head;
size_t *entries_head;
} increase_input_buffers_params_t;
// Prints a debug view of a `grid_t` to standard output.
void debug_print_grid(grid_t *grid)
{
printf("Grid ( row_len: %zu, data_head: %p, entries_head: %p, data_len: %zu, entries_len: %zu ) { ",
(grid->row_len), (void *)(grid->data_head), (void *)(grid->entries_head),
(grid->data_len), (grid->entries_len));
printf("data: [");
size_t str_counter = 0;
for (size_t i = 0; i < grid->data_len; i++)
{
if (i > 0)
{
printf(", ");
}
else
{
printf(" ");
}
_Bool is_char = 1;
if (grid->entries_len > 0 && str_counter < grid->entries_len) {
if ((grid->entries_head)[str_counter] == i) {
is_char = 0;
str_counter += 1;
}
}
if (is_char) {
printf("'%c'", (char)((grid->data_head)[i]));
} else {
printf("%" PRIgridDATA, (grid->data_head)[i]);
}
}
printf("], entries: [");
for (size_t i = 0; i < grid->entries_len; i++)
{
if (i > 0)
{
printf(", ");
}
else
{
printf(" ");
}
printf("%zu", (grid->entries_head)[i]);
}
printf("] }\n");
}
// Increases the data buffers used in `parse_input`.
//
// As reallocation will happen, the updated pointers and capacity are returned
// in an updated parameters object.
increase_input_buffers_params_t increase_input_buffers(increase_input_buffers_params_t params)
{
params.data_cap *= 2;
params.data_head = realloc(params.data_head, params.data_cap * sizeof(grid_data_t));
params.entries_head = realloc(params.entries_head, params.data_cap * sizeof(grid_data_t *));
if ((params.data_head == NULL) || (params.entries_head == NULL))
{
printf(
"Failed to reallocate bigger buffer for input data. Was trying to "
"allocate %zu entries, aborting.",
params.data_cap);
exit(EXIT_FAILURE);
}
return params;
}
// Parse the input from standard input.
//
// This function parses an extended matrix (C|l), where C is an n×m matrix, and
// l is an m-sized column vector. This input is given in standard input as
//
// <row length> <whitespace separated entries>
//
// 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...>
grid_t parse_input(void)
{
size_t row_len;
scanf("%zu", &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));
_Bool spaces = 1;
char scanned;
while (scanf("%c", &scanned) != EOF)
{
if (isspace(scanned))
{
spaces = 1;
}
else
{
// A little input sanitization.
if (!(isdigit(scanned) || (scanned == '-') || (scanned == '.')))
{
printf(
"Foreign character %c (%x) found when processing input, aborting.",
scanned, scanned);
exit(EXIT_FAILURE);
}
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;
}
spaces = 0;
// We're about to write a new character.
data_len += 1;
// Check if we need to resize the buffer.
if (data_len > data_cap)
{
increase_input_buffers_params_t params = {
.data_cap = data_cap,
.data_head = data_head,
.entries_head = entries_head,
};
params = increase_input_buffers(params);
data_cap = params.data_cap;
data_head = params.data_head;
entries_head = params.entries_head;
}
// 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
// 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))
{
printf("Number of entries is not consistent with provided row length. "
"Got row length of %zu, and read %zu entries. Aborting.",
row_len, entries_len);
// No need to free the buffers.
exit(EXIT_FAILURE);
}
grid_t grid = {
.row_len = row_len,
.data_head = data_head,
.entries_head = entries_head,
.data_len = data_len,
.entries_len = entries_len,
};
return grid;
}
int main(int argc, char *argv[])
{
grid_t grid = parse_input();
matrix_t grid = parse_matrix();
debug_print_grid(&grid);
return 0;
}

193
src/matrix.c Normal file
View File

@ -0,0 +1,193 @@
#include "../includes/matrix.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
// Prints a debug view of a `matrix_t` to standard output.
void debug_print_grid(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("data: [");
size_t str_counter = 0;
for (size_t i = 0; i < matrix->data_len; i++)
{
if (i > 0)
{
printf(", ");
}
else
{
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("] }\n");
}
// 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)
{
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))
{
printf(
"Failed to reallocate bigger buffer for input data. Was trying to "
"allocate %zu entries, aborting.",
bufs.data_cap);
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))
{
printf(
"Failed to reallocate matrix data buffers. Was trying to "
"allocate for %zu data entries, aborting.",
matrix->data_len);
exit(EXIT_FAILURE);
}
}
// 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...>
matrix_t parse_matrix(void)
{
size_t row_len;
scanf("%zu", &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));
_Bool spaces = 1;
char scanned;
while (scanf("%c", &scanned) != EOF)
{
if (isspace(scanned))
{
spaces = 1;
}
else
{
// A little input sanitization.
if (!(isdigit(scanned) || (scanned == '-') || (scanned == '.')))
{
printf(
"Foreign character %c (%x) found when processing input, aborting.",
scanned, scanned);
exit(EXIT_FAILURE);
}
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;
}
spaces = 0;
// We're about to write a new character.
data_len += 1;
// Check if we need to resize the buffer.
if (data_len > data_cap)
{
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;
}
// 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
// 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))
{
printf("Number of entries is not consistent with provided row length. "
"Got row length of %zu, and read %zu entries. Aborting.",
row_len, entries_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,
};
compact_buffers(&matrix);
return matrix;
}