Compare commits

...

2 Commits

Author SHA1 Message Date
Miguel M 79fa414c7d Big test data, to force reallocations 2023-04-22 19:43:56 +01:00
Miguel M eebca1914d Factored matrix (formerly grid) into a header
Includes also the memory compacting after all the input is read.
2023-04-22 19:42:58 +01:00
5 changed files with 288 additions and 206 deletions

View File

@ -1,4 +1,47 @@
3
1 1 -1
555 0.9 2
-55 2 1555
-55 2 1555
4 6 9
548 210 127
0 560 186
158 38 975
873 377 665
103 535 531
101 741 950
385 641 463
861 459 991
287 430 146
557 652 529
775 965 617
243 671 96
503 453 224
748 552 546
459 606 237
477 489 969
868 704 980
944 202 840
745 346 658
946 867 66
1 1 -1
555 0.9 2
-55 2 1555
4 6 9
548 210 127
0 560 186
158 38 975
873 377 665
103 535 531
101 741 950
385 641 463
861 459 991
287 430 146
557 652 529
775 965 617
243 671 96
503 453 224
748 552 546
459 606 237
477 489 969
868 704 980
944 202 840

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

@ -82,8 +82,9 @@ def clean():
@recipe(recipe_deps=[link],
conditions=[lambda: True],
info='Runs the main executable.')
info='Runs the main executable with the test matrix as input.')
def run():
sp.run(EXE_NAME)
with open(os.path.join(ROOT, 'data', 'test_mat.txt'), 'r') as test_input:
sp.run(os.path.join(ROOT, EXE_NAME), stdin=test_input)
sane_run(run)

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;
}