#include "matrixdense.h"
#include "matrixprod.h"
#include "sigmoid.h"
#include <iostream>
#include <chrono>
#include <algorithm>
// BSP 1_G
using namespace std;

int main()
{
    // ---- b ----
    MatrixDense const M(5,3);
    // Dense matrix, also initialized
    vector<double> const u{1,2,3};
    vector<double> f1 = M.Mult(u);
    cout << "M*u = ";
    for(size_t k=0; k<f1.size(); ++k)
    {
        cout << f1[k] << "  ";
    }
    cout << endl;

    vector<double> const v{-1,2,-3,4,-5};
    vector<double> f2 = M.MultT(v);
    cout << "M^t*v = ";
    for(size_t k=0; k<f2.size(); ++k)
    {
        cout << f2[k] << "  ";
    }
    cout << endl;

    // ---- c -----
    cout << "C" << endl;
    int nloops = 100;
    int n = 1000;
    MatrixDense const Mc(n,n);
    vector<double> x(n,2.0);
    vector<double> fa, fb;

    auto time1start = std::chrono::system_clock::now();
    for(int k=0; k<nloops; ++k)
    {
        fa = Mc.Mult(x);
    }
    auto time1end = std::chrono::system_clock::now();
    auto time1 = std::chrono::duration_cast<std::chrono::milliseconds>(time1end - time1start);

    auto time2start = std::chrono::system_clock::now();
    for(int k=0; k<nloops; ++k)
    {
        fb = Mc.MultT(x);
    }
    auto time2end = std::chrono::system_clock::now();
    auto time2 = std::chrono::duration_cast<std::chrono::milliseconds>(time2end - time2start);

    cout << "Zeit fuer Mult pro loop " << time1.count() << " ms" << endl;
    cout << "Zeit fuer MultT pro loop " << time2.count() << " ms" << endl;

    // Check ob beide Ergebnisse gleich sind
    vector<double> err(fa.size());
    for(size_t k = 0; k<fa.size(); ++k)
    {
        err[k] = abs(fa[k] - fb[k]);
    }
    double maxerr = *max_element(err.begin(), err.end());
    cout << "maximaler Fehler in Komponente " << maxerr << endl;

    // ----- d -----
    cout << "D" << endl;
    vector<double> u1(n,0);
    for(size_t k=0; k<u1.size(); ++k)
    {
        u1[k] = sigmoid(xk(k,u1.size()));
    }
    MatrixProd const Mb(u1,u1);
    vector<double> y(n,2.0);
    vector<double> fc, fd;

    auto time3start = std::chrono::system_clock::now();
    for(int k=0; k<nloops; ++k)
    {
        fc = Mb.Mult(y);
    }
    auto time3end = std::chrono::system_clock::now();
    auto time3 = std::chrono::duration_cast<std::chrono::milliseconds>(time3end - time3start);

    auto time4start = std::chrono::system_clock::now();
    for(int k=0; k<nloops; ++k)
    {
        fd = Mb.MultT(y);
    }
    auto time4end = std::chrono::system_clock::now();
    auto time4 = std::chrono::duration_cast<std::chrono::milliseconds>(time4end - time4start);

    cout << "Zeit fuer Mult pro loop " << time3.count() << " ms" << endl;
    cout << "Zeit fuer MultT pro loop " << time4.count() << " ms" << endl;

    // Check ob beide Ergebnisse gleich sind
    vector<double> err1(fc.size());
    for(size_t k = 0; k<fc.size(); ++k)
    {
        err1[k] = abs(fc[k] - fd[k]);
    }
    double maxerr1 = *max_element(err1.begin(), err1.end());
    cout << "maximaler Fehler in Komponente " << maxerr1 << endl;

    return 0;
}
