added lina_loadMatrixFromStream
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
.swp
|
.swp
|
||||||
test
|
test
|
||||||
|
test2
|
||||||
time
|
time
|
||||||
.vscode
|
.vscode
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
gcc test.c lina.c -o test -Wall -Wextra -g
|
gcc test.c lina.c -o test -Wall -Wextra -g
|
||||||
|
gcc test2.c lina.c -o test2 -Wall -Wextra -g
|
||||||
gcc time.c lina.c -o time -Wall -Wextra -O3
|
gcc time.c lina.c -o time -Wall -Wextra -O3
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "lina.h"
|
#include "lina.h"
|
||||||
|
|
||||||
#define check assert
|
#define check assert
|
||||||
@@ -27,12 +30,9 @@ void lina_dot(double *A, double *B, double *C, int m, int n, int l){
|
|||||||
|
|
||||||
pos += A[i*n + j] * B[j*l + k];
|
pos += A[i*n + j] * B[j*l + k];
|
||||||
|
|
||||||
// The usage of a support variable 'pos'
|
|
||||||
// is for safety purpose (non-zero C matrix)
|
|
||||||
C[i*l + k] = pos;
|
C[i*l + k] = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lina_add(double *A, double *B, double *C, int m, int n){
|
void lina_add(double *A, double *B, double *C, int m, int n){
|
||||||
@@ -97,4 +97,294 @@ void lina_transpose(double *A, double *B, int m, int n)
|
|||||||
|
|
||||||
B[1] = item;
|
B[1] = item;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 if an error occurred, 1 if an integer
|
||||||
|
// was scanned and -1 if a floating point was scanned.
|
||||||
|
static int scanValue(FILE *fp, char *buffer, int max_length, char first, char *final, char **error)
|
||||||
|
{
|
||||||
|
assert(fp != NULL && buffer != NULL && error != NULL);
|
||||||
|
assert(isdigit(first));
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
char c = first;
|
||||||
|
|
||||||
|
// Scan the integer portion of
|
||||||
|
// the numeric value and copy it
|
||||||
|
// into the buffer.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(n == max_length)
|
||||||
|
// ERROR: Internal buffer is too small to hold
|
||||||
|
// the representation of this item.
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
buffer[n++] = c;
|
||||||
|
|
||||||
|
c = getc(fp);
|
||||||
|
}
|
||||||
|
while(c != EOF && isdigit(c));
|
||||||
|
|
||||||
|
// Did the integer part end with
|
||||||
|
// a dot?
|
||||||
|
_Bool dot = (c == '.');
|
||||||
|
|
||||||
|
// Now scan and copy the decimal
|
||||||
|
// part of the numeric value if
|
||||||
|
// a dot was found.
|
||||||
|
if(dot)
|
||||||
|
{
|
||||||
|
if(n == max_length)
|
||||||
|
// ERROR: Internal buffer is too small to hold
|
||||||
|
// the representation of this item.
|
||||||
|
// (The dot doesn't fit.)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
buffer[n++] = '.';
|
||||||
|
|
||||||
|
c = getc(fp);
|
||||||
|
|
||||||
|
if(!isdigit(c))
|
||||||
|
// ERROR: Got something other than a
|
||||||
|
// digit after the dot.
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(n == max_length)
|
||||||
|
// ERROR: Internal buffer is too small
|
||||||
|
// to hold the representation of
|
||||||
|
// this item.
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
buffer[n++] = c;
|
||||||
|
|
||||||
|
c = getc(fp);
|
||||||
|
}
|
||||||
|
while(c != EOF && isdigit(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[n] = '\0';
|
||||||
|
|
||||||
|
if(final != NULL)
|
||||||
|
*final = c;
|
||||||
|
|
||||||
|
return dot ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double *lina_loadMatrixFromStream(FILE *fp, int *width, int *height, char **error)
|
||||||
|
{
|
||||||
|
assert(width != NULL && height != NULL);
|
||||||
|
|
||||||
|
if(fp == NULL)
|
||||||
|
fp = stdin;
|
||||||
|
|
||||||
|
char *dummy;
|
||||||
|
if(error == NULL)
|
||||||
|
error = &dummy;
|
||||||
|
else
|
||||||
|
*error = NULL;
|
||||||
|
|
||||||
|
char c = getc(fp);
|
||||||
|
|
||||||
|
while(c != EOF && isspace(c))
|
||||||
|
c = getc(fp);
|
||||||
|
|
||||||
|
if(c == EOF)
|
||||||
|
{
|
||||||
|
// ERROR: Stream ended before a matrix was
|
||||||
|
// found.
|
||||||
|
*error = "Stream ended before a matrix was found";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c != '[')
|
||||||
|
{
|
||||||
|
// ERROR: Was expected a '[' as the first
|
||||||
|
// character of a matrix, but got
|
||||||
|
// something else instead.
|
||||||
|
*error = "Got something other than a matrix "
|
||||||
|
"where one was expected";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = getc(fp);
|
||||||
|
|
||||||
|
// Skip spaces before the first element.
|
||||||
|
while(c != EOF && isspace(c))
|
||||||
|
c = getc(fp);
|
||||||
|
|
||||||
|
if(c == EOF)
|
||||||
|
{
|
||||||
|
// ERROR: Stream ended where a numeric value
|
||||||
|
// was expected.
|
||||||
|
*error = "Stream ended where a numeric value "
|
||||||
|
"was expected";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
double *matrix = malloc(sizeof(matrix[0]) * 64);
|
||||||
|
|
||||||
|
if(matrix == NULL)
|
||||||
|
{
|
||||||
|
// ERROR: Insufficient memory.
|
||||||
|
*error = "Insufficient memory";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int capacity = 64, size = 0,
|
||||||
|
w = -1, i = 0, j = 0;
|
||||||
|
|
||||||
|
if(c != ']')
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if(!isdigit(c))
|
||||||
|
{
|
||||||
|
// ERROR: Got something other than a digit
|
||||||
|
// where a numeric value was expected.
|
||||||
|
*error = "Got something other than a numeric "
|
||||||
|
"value where one was expected";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric values can't be represented
|
||||||
|
// in strings bigger than this buffer
|
||||||
|
// since they need to be copied in it
|
||||||
|
// to be converted to actual numeric
|
||||||
|
// variables.
|
||||||
|
char buffer[128];
|
||||||
|
|
||||||
|
int res = scanValue(fp, buffer, sizeof(buffer), c, &c, error);
|
||||||
|
|
||||||
|
if(res == 0)
|
||||||
|
// Failed to scan the value, abort.
|
||||||
|
// NOTE: The error was already reported.
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert(res == 1 || res == -1);
|
||||||
|
|
||||||
|
// Make sure the matrix has enough space.
|
||||||
|
if(size == capacity)
|
||||||
|
{
|
||||||
|
int new_capacity = capacity * 2;
|
||||||
|
|
||||||
|
double *temp = realloc(matrix, sizeof(double) * new_capacity);
|
||||||
|
|
||||||
|
if(temp == NULL)
|
||||||
|
{
|
||||||
|
// ERROR: Insufficient memory.
|
||||||
|
*error = "Insufficient memory";
|
||||||
|
free(matrix);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix = temp;
|
||||||
|
capacity = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
double casted;
|
||||||
|
|
||||||
|
if(res == 1)
|
||||||
|
casted = (double) strtoll(buffer, NULL, 10);
|
||||||
|
else
|
||||||
|
casted = strtod(buffer, NULL);
|
||||||
|
|
||||||
|
if(errno)
|
||||||
|
{
|
||||||
|
// ERROR: Failed to convert a numeric value
|
||||||
|
// from it's string form to a numeric
|
||||||
|
// variable.
|
||||||
|
*error = "Failed to convert string to number";
|
||||||
|
free(matrix);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix[size++] = casted;
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
while(c != EOF && isspace(c))
|
||||||
|
c = getc(fp);
|
||||||
|
|
||||||
|
if(c == ']' || c == ',')
|
||||||
|
{
|
||||||
|
// The matrix's row just ended.
|
||||||
|
|
||||||
|
if(w == -1)
|
||||||
|
// This was the first row.
|
||||||
|
w = i;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This wasn't the first row,
|
||||||
|
// so it's possible that it's
|
||||||
|
// length is different from the
|
||||||
|
// previous ones.
|
||||||
|
assert(w > -1);
|
||||||
|
|
||||||
|
if(i != w)
|
||||||
|
{
|
||||||
|
// ERROR: The j-th row has the wrong
|
||||||
|
// number of elements.
|
||||||
|
if(i < w)
|
||||||
|
*error = "Matrix row is too short";
|
||||||
|
else
|
||||||
|
*error = "Matrix row is too long";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
j += 1;
|
||||||
|
|
||||||
|
if(c == ']')
|
||||||
|
// The whole matrix ended!
|
||||||
|
break;
|
||||||
|
|
||||||
|
c = getc(fp);
|
||||||
|
|
||||||
|
while(c != EOF && isspace(c))
|
||||||
|
c = getc(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == EOF)
|
||||||
|
{
|
||||||
|
// ERROR: Stream ended inside a matrix, where
|
||||||
|
// either ',', ']' or a numeric value was
|
||||||
|
// expected.
|
||||||
|
*error = "Stream ended inside a matrix, where either "
|
||||||
|
"',', ']' or a numeric value was expected";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size == 0)
|
||||||
|
{
|
||||||
|
free(matrix);
|
||||||
|
*error = "Empty matrix";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the internal fragmentation is too much,
|
||||||
|
// return a dynamic memory region with the
|
||||||
|
// exact size instead of the buffer used to
|
||||||
|
// build the matrix.
|
||||||
|
int fragm_threshold = 30; // (It's a percentage)
|
||||||
|
|
||||||
|
if(100.0 * size/capacity < fragm_threshold)
|
||||||
|
{
|
||||||
|
int new_capacity = (size == 0) ? 1 : size;
|
||||||
|
|
||||||
|
double *temp = realloc(matrix, new_capacity * sizeof(double));
|
||||||
|
|
||||||
|
if(temp != NULL)
|
||||||
|
matrix = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
*width = w;
|
||||||
|
*height = j;
|
||||||
|
|
||||||
|
return matrix;
|
||||||
}
|
}
|
||||||
@@ -13,4 +13,9 @@ void lina_scale(double *A, double *B, double k, int m, int n);
|
|||||||
|
|
||||||
// Evaluate B = A^t (the transpose of A) where A is m by n.
|
// Evaluate B = A^t (the transpose of A) where A is m by n.
|
||||||
// NOTE: B can be the same location of A.
|
// NOTE: B can be the same location of A.
|
||||||
void lina_transpose(double *A, double *B, int m, int n);
|
void lina_transpose(double *A, double *B, int m, int n);
|
||||||
|
|
||||||
|
// Load from a stream a matrix in the form [a b c.. , d e f.. , ...]
|
||||||
|
// where a,b,c,.. are either integer or floating point values.
|
||||||
|
// The returned pointer must be deallocated using `free`.
|
||||||
|
double *lina_loadMatrixFromStream(FILE *fp, int *width, int *height, char **error);
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "lina.h"
|
||||||
|
|
||||||
|
#define check assert
|
||||||
|
|
||||||
|
// This program tests the matrix-loading
|
||||||
|
// function lina_loadMatrixFromStream.
|
||||||
|
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char *src;
|
||||||
|
double *matrix;
|
||||||
|
int w, h;
|
||||||
|
} load_tests[] = {
|
||||||
|
{ .src = "", .matrix = NULL },
|
||||||
|
{ .src = " [] ", .matrix = NULL },
|
||||||
|
{ .src = " [1] ", .matrix = (double[]) {1}, .w = 1, .h = 1 },
|
||||||
|
{ .src = "[1 2 3]", .matrix = (double[]) {1, 2, 3}, .w = 3, .h = 1 },
|
||||||
|
{ .src = "[1, 2, 3]", .matrix = (double[]) {1, 2, 3}, .w = 1, .h = 3 },
|
||||||
|
{ .src = "[1 2 3, 4 5 6, 7 8 9]", .matrix = (double[]) {1, 2, 3, 4, 5, 6, 7, 8, 9}, .w = 3, .h = 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int total = sizeof(load_tests) / sizeof(load_tests[0]);
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < total; i += 1)
|
||||||
|
{
|
||||||
|
char *src = load_tests[i].src;
|
||||||
|
|
||||||
|
FILE *stream = fmemopen(src, strlen(src), "r");
|
||||||
|
check(stream != NULL);
|
||||||
|
|
||||||
|
int w, h;
|
||||||
|
char *err;
|
||||||
|
double *M = lina_loadMatrixFromStream(stream, &w, &h, &err);
|
||||||
|
|
||||||
|
double *expected_M = load_tests[i].matrix;
|
||||||
|
|
||||||
|
if(expected_M == NULL)
|
||||||
|
{
|
||||||
|
// Was expected a failure.
|
||||||
|
if(M == NULL)
|
||||||
|
{
|
||||||
|
// And we got one.
|
||||||
|
fprintf(stderr, "Test %d passed.\n", i);
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// Yet the routine succeded.
|
||||||
|
fprintf(stderr, "Test %d failed:\n\tWas expected a failure but "
|
||||||
|
"the routine succeded to parse \"%s\"\n", i, src);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Was expected a success.
|
||||||
|
if(M == NULL)
|
||||||
|
// But we got a failure.
|
||||||
|
fprintf(stderr, "Test %d failed:\n\tCouldn't parse \"%s\" (%s)\n", i, src, err);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// And we got a success! Now we need to check
|
||||||
|
// that the parsed matrix is the right one.
|
||||||
|
|
||||||
|
int expected_w = load_tests[i].w;
|
||||||
|
int expected_h = load_tests[i].h;
|
||||||
|
|
||||||
|
if(expected_w != w || expected_h != h)
|
||||||
|
|
||||||
|
fprintf(stderr, "Test %d failed:\n\tParsing \"%s\" resulted in a "
|
||||||
|
"matrix %dx%d, where a matrix %dx%d was expected\n",
|
||||||
|
i, src, h, w, expected_h, expected_w);
|
||||||
|
|
||||||
|
else if(memcmp(M, expected_M, sizeof(M[0])*w*h))
|
||||||
|
|
||||||
|
fprintf(stderr, "Test %d failed:\n\tParsing \"%s\" resulted in the "
|
||||||
|
"wrong matrix\n", i, src);
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test %d passed.\n", i);
|
||||||
|
passed += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(M);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, " %d total, %d passed, %d failed\n", total, passed, total - passed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user