#include <vector>
#include <chrono>
#include <iostream>
#include <omp.h>
#include <cstdlib>
#include <ctime>

// Zufallsmatrizen erzeugen
void generateRandomMatrices(int n,
                            std::vector<std::vector<double>>& A,
                            std::vector<std::vector<double>>& B)
{
    std::srand(static_cast<unsigned int>(std::time(nullptr)));
    A.resize(n, std::vector<double>(n));
    B.resize(n, std::vector<double>(n));

    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
        {
            A[i][j] = static_cast<double>(rand()) / RAND_MAX * 10.0;
            B[i][j] = static_cast<double>(rand()) / RAND_MAX * 10.0;
        }
}

// Serielle Matrizenmultiplikation
std::vector<std::vector<double>> matmul_serial(
    const std::vector<std::vector<double>>& A,
    const std::vector<std::vector<double>>& B)
{
    int n = A.size();
    int m = B[0].size();
    int p = B.size();
    std::vector<std::vector<double>> C(n, std::vector<double>(m, 0.0));

    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            for (int k = 0; k < p; ++k)
                C[i][j] += A[i][k] * B[k][j];

    return C;
}

// Parallele Zeilenzerlegung
std::vector<std::vector<double>> matmul_row_parallel(
    const std::vector<std::vector<double>>& A,
    const std::vector<std::vector<double>>& B)
{
    int n = A.size();
    int m = B[0].size();
    int p = B.size();
    std::vector<std::vector<double>> C(n, std::vector<double>(m, 0.0));

#pragma omp parallel for
    for (int i = 0; i < n; ++i)      // Zeilen parallel
        for (int j = 0; j < m; ++j)
            for (int k = 0; k < p; ++k)
                C[i][j] += A[i][k] * B[k][j];

    return C;
}

// -----------------------------
int main()
{
    int N = 500;
    std::vector<std::vector<double>> A, B;
    generateRandomMatrices(N, A, B);

    // Serielle Version
    auto start = std::chrono::steady_clock::now();
    auto C_serial = matmul_serial(A, B);
    auto end = std::chrono::steady_clock::now();
    std::cout << "Serial multiplication took: "
              << std::chrono::duration<double>(end - start).count() << "s\n";

    // Zeilenparallel
    start = std::chrono::steady_clock::now();
    auto C_row = matmul_row_parallel(A, B);
    end = std::chrono::steady_clock::now();
    std::cout << "Row-parallel multiplication took: "
              << std::chrono::duration<double>(end - start).count() << "s\n";

    // Vergleich
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            if (C_serial[i][j] != C_row[i][j])
            {
                std::cout << "Mismatch at " << i << "," << j << "\n";
                return 1;
            }

    std::cout << "All results match!\n";
    return 0;
}
