#include "include/helpers.hpp"
#include "include/solvers.hpp"

#include <chrono>
#include <fstream>
#include <iomanip>

using namespace std;


vector<double> appl_general_algorithm(const vector<double>& g){
    int N = g.size() + 1;
    vector<double> a(N-1, -1.0);
    vector<double> b(N-1, 2.0);
    vector<double> c(N-1, -1.0);
    return general_algorithm(a, b, c, g);
}

double time_function(const vector<double>& g, vector<double> (*func)(const vector<double>&), const int n_iter=100){
    auto start = chrono::high_resolution_clock::now();
    for(int i = 0; i < n_iter; i++){
        func(g);
    }
    auto end = chrono::high_resolution_clock::now();
    chrono::duration<double> elapsed = end - start;
    return elapsed.count() / n_iter;
}

int main(int argc, char* argv[]) {
    int n_iter = 100;
    if (argc > 1) { // Get number of iterations from command line argument
        n_iter = atoi(argv[1]);
    }

    int N = 1000;
    vector<double> g = get_g(get_x(N));
    cout << "Relative error between solvers: " << relative_error(special_algorithm(g), appl_general_algorithm(g)) << endl;
    cout << "Relative error between solvers (optimized): " << relative_error(optimal_algorithm_opt(g), appl_general_algorithm(g)) << endl;

    string filename = "timing_results_" + to_string(n_iter) + "_iterations.csv";
    ofstream outfile(filename);
    outfile << "N,Time (Special),Time (Optimized),Time (General)\n";
    for(int power = 1; power <= 6; power++){
        N = pow(10, power);
        g = get_g(get_x(N));
        double time_optimal = time_function(g, special_algorithm, n_iter);
        double time_optimal_opt = time_function(g, optimal_algorithm_opt, n_iter);
        double time_general = time_function(g, appl_general_algorithm, n_iter);
        cout << "Average time for specialized algorithm over " << N << " values: " << time_optimal << " seconds" << endl;
        cout << "Average time for optimized algorithm over " << N << " values: " << time_optimal_opt << " seconds" << endl;
        cout << "Average time for general algorithm over " << N << " values: " << time_general << " seconds" << endl;

        outfile << N << "," << scientific << setprecision(10) << time_optimal << "," << time_optimal_opt << "," << time_general << endl;
    }
    outfile.close();

    return 0;
}