#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>

#define FILENAME_A "macierzA.dat"
#define FILENAME_B "macierzB.dat"
#define BUFLEN 1024

struct dim
{
    int rows;
    int columns;
};

void matrix_bzero(double **matrix, int row, int column)
{
    int i, j;

    for (i = 0; i < row; i++)
	for (j = 0; j < column; j++)
	    matrix[i][j] = 0.0;
}

double **matrix_new(int row, int column)
{
    double **tmp;
    int i;

    tmp = malloc(row * sizeof (double *));
    if (tmp == NULL)
	return NULL;

    tmp[0] = malloc(row * column * sizeof (double));
    if (tmp[0] == NULL)
    {
	free(tmp);
	return NULL;
    }

    for (i = 1; i < row; i++)
	tmp[i] = tmp[i - 1] + column;

    matrix_bzero(tmp, row, column);

    return tmp;
}

void matrix_free(double **matrix)
{
    free(matrix[0]);
    free(matrix);
}

void matrix_fill_from_user(double **matrix, int r, int c, const char *desc)
{
    int i, j;

    printf("Wypelnij macierz %s rozmiaru %d x %d\n", desc, r, c);
    for (i = 0; i < r; i++)
	for (j = 0; j < c; j++)
	{
	    printf("Podaj %s[%d,%d]: ", desc, i + 1, j + 1);
	    scanf("%lf", &matrix[i][j]);
	}
}

double **matrix_new_from_multiply(double **a, double **b, int m, int n, int r, const char *desc)
{
    int i, j, k;
    double **tmp = matrix_new(m, r);

    for (i = 0; i < m; i++)
	for (j = 0; j < n; j++)
	    for (k = 0; k < r; k++)
		tmp[i][k] += a[i][j] * b[j][k];

    return tmp;
}

void matrix_print(double **matrix, int r, int c, const char *desc)
{
    int i, j;

    printf("Macierz %s:\n", desc);
    for (j = 0; j < c; j++)
	printf("--------");
    printf("\n");

    for (i = 0; i < r; i++)
    {
	for (j = 0; j < c; j++)
	    printf("%7.2f ", matrix[i][j]);
	printf("\n");
    }

    for (j = 0; j < c; j++)
	printf("--------");
    printf("\n");
}

struct dim matrix_get_dim_from_file(const char *filename)
{
    struct dim tmp = { 0, 0 };
    int i = 0, j = 0;
    char *buf = NULL;
    size_t read = 0;
    FILE *fp;

    if ((fp = fopen(filename, "r")) == NULL)
	return tmp;

    while (getline(&buf, &read, fp) != EOF)
    {
	sscanf(buf, "dim %d %d", &i, &j);
	if (i > 0 && j > 0)
	{
	    tmp.rows = i;
	    tmp.columns = j;
	    break;
	}
    }

    fclose(fp);

    return tmp;
}

void matrix_fill_from_file(double **matrix, int r, int c, const char *filename)
{
    int i, j;
    double a;
    char *buf = NULL;
    size_t read = 0;
    FILE *fp;

    if ((fp = fopen(filename, "r")) == NULL)
	return;

    while (getline(&buf, &read, fp) != EOF)
    {
	sscanf(buf, "set %d %d %lf", &i, &j, &a);
	if (i > 0 && i <= r && j > 0 && j <= c)
	    matrix[i - 1][j - 1] = a;
    }

    fclose(fp);
}

int main(int argc, char **argv)
{
    int m, n, r;
    struct dim dimA, dimB;
    double **a, **b, **c;
    char *file1 = FILENAME_A;
    char *file2 = FILENAME_B;

    if (argc > 1 && argv[1])
	file1 = argv[1];

    if (argc > 2 && argv[2])
	file2 = argv[2];

    dimA = matrix_get_dim_from_file(file1);
    dimB = matrix_get_dim_from_file(file2);

    if (dimA.rows < 1 || dimA.columns < 1)
    {
	fprintf(stderr, "Problemy z odczytaniem %s?\n", file1);
	exit(1);
    }
    if (dimB.rows < 1 || dimB.columns < 1)
    {
	fprintf(stderr, "Problemy z odczytaniem %s?\n", file2);
	exit(1);
    }

    if (dimA.columns != dimB.rows)
    {
	fprintf(stderr, "Liczba kolumn macierzy w %s winna być równa liczbie wierszy macierzy w %s\n", file1, file2);
	exit(1);
    }

    m = dimA.rows;
    n = dimB.rows;
    r = dimB.columns;

    if ((a = matrix_new(m, n)) == NULL || (b = matrix_new(n, r)) == NULL)
    {
	fprintf(stderr, "malloc failed.\n");
	exit(1);
    }

    matrix_fill_from_file(a, m, n, file1);
    matrix_print(a, m, n, "A");

    matrix_fill_from_file(b, n, r, file2);
    matrix_print(b, n, r, "B");

    c = matrix_new_from_multiply(a, b, m, n, r, "C");

    if (c == NULL)
    {
	fprintf(stderr, "something went wrong in matrix_new_from_multiply()\n");
	exit(1);
    }

    matrix_print(c, m, r, "C");

    matrix_free(a);
    matrix_free(b);
    matrix_free(c);

    return 0;
}
