#include "mylib.h"
#include <cassert>
#include <chrono>           // timing
#include <cmath>            // sqrt()
#include <cstdlib>          // atoi()
#include <cstring>          // strncmp()
#include <ctime>
#include <iostream>
#include <sstream>
using namespace std;
using namespace std::chrono;  // timing

int main(int argc, char **argv)
{
    int const NLOOPS = 50;        // chose a value such that the benchmark runs at least 10 sec.
    unsigned int N = 50000001;
//##########################################################################
//   Read Paramater from command line  (C++ style)
    cout << "Checking command line parameters for: -n <number> " << endl;
    for (int i = 1; i < argc; i++)
    {
        cout << " arg[" << i << "] = " << argv[i] << endl;
        if (std::strncmp(argv[i], "-n", 2) == 0 && i + 1 < argc) // found "-n" followed by another parameter
        {
            N = static_cast<unsigned int>(atoi(argv[i + 1]));
        }
        else
        {
            cout << "Corect call: " << argv[0] << " -n  <number>\n";
        }
    }

    cout << "\nN = " << N << endl;

//##########################################################################
//  Memory allocation
    cout << "Memory allocation\n";

    vector<double> x(N), y(N);

    cout.precision(2);
    cout << 2.0 * N *sizeof(x[0]) / 1024 / 1024 / 1024 << " GByte Memory allocated\n";
    cout.precision(6);

//##########################################################################
//  Data initialization
//  Special:  x_i = i+1;  y_i = 1/x_i  ==> <x,y> == N
    for (unsigned int i = 0; i < N; ++i)
    {
        x[i] = i + 1;
        y[i] = 1.0 / x[i];
    }

//##########################################################################
    cout << "\nStart Benchmarking\n";

    auto t1 = system_clock::now(); // start timer
// Do calculation
    double sk(0.0),ss(0.0);
    for (int i = 0; i < NLOOPS; ++i)
    {
        sk = scalar(x, y);
        //sk = scalar_cblas(x, y);
      //sk = norm(x);
        ss += sk;                   // prevents the optimizer from removing unused calculation results.
    }

    auto t2 = system_clock::now();  // stop timer
    auto duration = duration_cast<microseconds>(t2 - t1);        // duration in microseconds
    double t_diff = static_cast<double>(duration.count()) / 1e6; // overall duration in seconds
    t_diff = t_diff/NLOOPS;                                      // duration per loop seconds
    
    //assert(std::abs(ss/NLOOPS-sk)<1e-5);  // avoids unsafe floating point comparison "==" 

//##########################################################################
// Check the correct result
    cout << "\n <x,y> = " << sk << endl;
    if (static_cast<unsigned int>(sk) != N)
    {
        cout << "  !!   W R O N G  result   !!\n";
    }
    cout << endl;

//##########################################################################
// Timings  and Performance
    cout << endl;
    cout.precision(2);
    cout << "Timing in sec. : " << t_diff << endl;
    cout << "GFLOPS         : " << 2.0 * N / t_diff / 1024 / 1024 / 1024 << endl;
    cout << "GiByte/s       : " << 2.0 * N / t_diff / 1024 / 1024 / 1024 * sizeof(x[0]) << endl;

//##########################################################################
    cout << "\nStart Benchmarking norm\n";

    auto t3 = system_clock::now(); // start timer
// Do calculation
    double ss2(0.0);
    for (int i = 0; i < NLOOPS; ++i)
    {
        auto sk = sqrt(scalar(x, x));
        //auto sk = norm(x);         // the same timing as scalar(x, x)
        ss2 += sk;                   // prevents the optimizer from removing unused calculation results.
    }

    auto t4 = system_clock::now();  // stop timer
    auto duration2 = duration_cast<microseconds>(t4 - t3);        // duration in microseconds
    double t_diff2 = static_cast<double>(duration2.count()) / 1e6; // overall duration in seconds
    t_diff2 = t_diff2/NLOOPS;                                      // duration per loop seconds
    
    //assert(std::abs(ss/NLOOPS-sk)<1e-5);  // avoids unsafe floating point comparison "==" 
    cout << "ss(norm): " << ss2 << endl;
    cout << "Timing in sec. : " << t_diff2 << endl;

//##########################################################################


    return 0;
}  // memory for x and y will be deallocated by their destructors
