#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "matrixOp.h"

// Erstellt eine Matrix mit allen Einträgen 0
Matrix* CreateMatrixZero(int rows, int cols) {
   Matrix *m = malloc(sizeof(Matrix)); // Speicher für Matrix-Struktur
   m->rows = rows;
   m->cols = cols;
   m->data = malloc(rows * sizeof(double*)); // Speicher für Zeilenzeiger
   for (int i = 0; i < rows; i++) {
       m->data[i] = calloc(cols, sizeof(double)); // Speicher für jede Zeile, initialisiert mit 0
   }
   return m;
}


// Erstellt eine Matrix, deren Einträge mit startValue beginnen und dann hochzählen
Matrix* CreateMatrix(int rows, int cols, double startValue) {
   Matrix *m = CreateMatrixZero(rows, cols);
   double val = startValue;
   for (int i = 0; i < rows; i++) {
       for (int j = 0; j < cols; j++) {
           m->data[i][j] = val++;
       }
   }
   return m;
}

// Gibt den Speicher einer Matrix wieder frei
void FreeMatrix(Matrix *m) {
   for (int i = 0; i < m->rows; i++) {
       free(m->data[i]); // Jede Zeile freigeben
   }
   free(m->data); // Zeilenzeiger-Array freigeben
   free(m);       // Matrix-Struktur freigeben
}

// Gibt die Matrix schön formatiert auf der Konsole aus
void PrintMatrix(Matrix *m) {
   for (int i = 0; i < m->rows; i++) {
       for (int j = 0; j < m->cols; j++) {
           printf("%6.2f ", m->data[i][j]);
       }
       printf("\n");
   }
}

// Addiert zwei Matrizen gleicher Größe
Matrix* AddMatrix(Matrix *a, Matrix *b) {
   if (a->rows != b->rows || a->cols != b->cols) return NULL; // Nur gleiche Größe erlaubt
   Matrix *m = CreateMatrixZero(a->rows, a->cols);
   for (int i = 0; i < m->rows; i++) {
       for (int j = 0; j < m->cols; j++) {
           m->data[i][j] = a->data[i][j] + b->data[i][j];
       }
   }
   return m;
}

// Subtrahiert zwei Matrizen gleicher Größe
Matrix* SubMatrix(Matrix *a, Matrix *b) {
   if (a->rows != b->rows || a->cols != b->cols) return NULL;
   Matrix *m = CreateMatrixZero(a->rows, a->cols);
   for (int i = 0; i < m->rows; i++) {
       for (int j = 0; j < m->cols; j++) {
           m->data[i][j] = a->data[i][j] - b->data[i][j];
       }
   }
   return m;
}

// Transponiert eine Matrix (Zeilen und Spalten vertauschen)
Matrix* TransposeMatrix(Matrix *a) {
   Matrix *m = CreateMatrixZero(a->cols, a->rows); // Zeilen und Spalten vertauscht
   for (int i = 0; i < a->rows; i++) {
       for (int j = 0; j < a->cols; j++) {
           m->data[j][i] = a->data[i][j];
       }
   }
   return m;
}

// Multipliziert zwei Matrizen (a: m x n, b: n x p)
Matrix* MulMatrix(Matrix *a, Matrix *b) {
   if (a->cols != b->rows) return NULL; // Nur möglich, wenn Spalten von a == Zeilen von b
   Matrix *m = CreateMatrixZero(a->rows, b->cols);
   for (int i = 0; i < a->rows; i++) {
       for (int j = 0; j < b->cols; j++) {
           for (int k = 0; k < a->cols; k++) {
               m->data[i][j] += a->data[i][k] * b->data[k][j];
           }
       }
   }
   return m;
}

// Berechnet die Determinante einer 2x2-Matrix
double DetMatrix(Matrix *m) {
   if (m->rows != m->cols) return 0; // Nur quadratische Matrizen
   if (m->rows == 2) {
       // Formel: ad - bc
       return m->data[0][0] * m->data[1][1] - m->data[0][1] * m->data[1][0];
   }
   // Für größere Matrizen nicht implementiert
   return 0;
}