#include <algorithm>
#include <chrono>
#include <cmath>
#include <execution>
#include <future>
#include <matplot/matplot.h>

#include "Gauss.h"
#include "Matrix.h"
#include "Solver.h"

static constexpr int EXECUTION_COUNT = 100;
static constexpr int MATRIX_MAX_SIZE = 300;

static unsigned int GetRandomInt() {
	return rand() % MATRIX_MAX_SIZE + 1;
}

static Matrix GetRandomMatrix(std::size_t a_Raw, std::size_t a_Column) {
	Matrix matrix {a_Raw, a_Column};

	for (std::size_t i = 0; i < matrix.GetRawCount(); i++) {
		for (std::size_t j = 0; j < matrix.GetColumnCount(); j++) {
			matrix.at(i, j) = GetRandomInt();
		}
	}

	return matrix;
}

std::vector<double> GaussJordan(const std::vector<double>& x) {
	std::vector<double> y;

	std::for_each(x.begin(), x.end(), [&y](double size) {
		auto start = std::chrono::system_clock::now();

		for (int j = 0; j < EXECUTION_COUNT; j++) {
			Matrix mat = GetRandomMatrix(size, size);
			Gauss::GaussJordan(mat, false, false);
		}

		auto end = std::chrono::system_clock::now();
		std::chrono::duration<double> elapsed_seconds = end - start;
		std::cout << "S " << size << "\n";
		y.push_back(elapsed_seconds.count() / static_cast<double>(EXECUTION_COUNT));
	});

	return y;
}


std::vector<double> GaussJordanReduite(const std::vector<double>& x) {
	std::vector<double> y;

	std::for_each(x.begin(), x.end(), [&y](double size) {
		auto start = std::chrono::system_clock::now();

		for (int j = 0; j < EXECUTION_COUNT; j++) {
			Matrix mat = GetRandomMatrix(size, size);
			Gauss::GaussJordan(mat, true, false);
		}

		auto end = std::chrono::system_clock::now();
		std::chrono::duration<double> elapsed_seconds = end - start;
		std::cout << "R " << size << "\n";
		y.push_back(elapsed_seconds.count() / static_cast<double>(EXECUTION_COUNT));
	});

	return y;
}

int main() {
	srand(time(0));

	int start = 1;

	std::vector<double> x = matplot::linspace(start, MATRIX_MAX_SIZE, MATRIX_MAX_SIZE - start + 1);
	//std::vector<double> x = {5000};
	std::vector<double> y, y1, y2, y3;

	// y2.resize(x.size());

	{
		auto result1 = std::async(std::launch::async, &GaussJordan, x);
		auto result2 = std::async(std::launch::async, &GaussJordanReduite, x);
		y = result1.get();
		y1 = result2.get();
	}


	std::cout << "Fini !\n";


	// std::transform(x.begin(), x.end(), y2.begin(), [](double x) { return 1.0 / (100.0 * 100.0) * 0.6 * x * x; });

	matplot::title("Echelonnage de matrices");
	matplot::xlabel("Taille des matrices");
	matplot::ylabel("Temps d'exécution (s)");

	matplot::hold(matplot::on);
	matplot::plot(x, y);
	matplot::plot(x, y1);

	auto l = matplot::legend({"Echelonnage non réduit", "Echelonnage réduit", "Echelonnage non réduit normalisé", "Echelonnage réduit normalisé"});
	l->location(matplot::legend::general_alignment::topleft);

	matplot::show();
	return 0;
}