#include "bsp_5_2_lib.h"

#include <cassert>
#include <fstream>
#include <execution>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <string>
#ifdef _OPENMP
#include <omp.h>
#endif
#include <vector>
#include <cmath>
using namespace std;


void meandevvec(vector<int> x, float &a, float &g, float &h, float &d)
{
    a = 0;
#pragma omp parallel for default(none) shared(x) reduction(+:a)
    for(size_t k=1; k<=x.size(); ++k)
    {
        a = a+x.at(k-1);
    }
    a = a/x.size();

    g = 1;
#pragma omp parallel for default(none) shared(x) reduction(*:g)
    for(size_t k=1; k<=x.size(); ++k)
    {
        g = g*pow(x.at(k-1),1.0/x.size());
    }

    h = 0;
#pragma omp parallel for default(none) shared(x) reduction(+:h)
    for(size_t k=1; k<=x.size(); ++k)
    {
        h = h + 1.0/x.at(k-1);
    }
    h = x.size()/h;

    d = 0;
#pragma omp parallel for default(none) shared(x,a) reduction(+:d)
    for(size_t k=1; k<=x.size(); ++k)
    {
        d = d + pow(x.at(k-1)-a,2);
    }
    d = d/x.size();
    d = sqrt(d);

    return;
}


void minmaxvec(vector<int> &x, int &minv, int &maxv)
{
	minv = numeric_limits<int>::max();
    maxv = numeric_limits<int>::min();

#pragma omp parallel for default(none) shared(x) reduction(min:minv) reduction(max:maxv)
    for (size_t i = 0; i < x.size(); ++i)
    {
        minv = min(minv, x[i]);
        maxv = max(maxv, x[i]);
    }
	return;
}


void meandevmaxminvec(std::vector<int> &x, float &a, float &g, float &h, float &d, float &minv, float &maxv)
{
	const size_t n = x.size();

    float sum = reduce(execution::par, x.begin(), x.end());
    float logsum = transform_reduce(execution::par, x.begin(), x.end(), 0.0, plus<>(), [](float y){ return log(y); });
    float invsum = transform_reduce(execution::par, x.begin(), x.end(), 0.0, plus<>(), [](float y){ return 1.0/y; });

    a = sum / n;
    g = exp(logsum / n);
    h = n / invsum;
    
    // for calculating the deviation - splitting of the square in the sum
    float sq_sum = transform_reduce(execution::par, x.begin(), x.end(), 0.0, plus<>(), [](float y){ return y * y; });
    d = sqrt(sq_sum / n - a*a);
    
	auto [min_it, max_it] = minmax_element(execution::par, x.begin(), x.end());
	minv = *min_it;
	maxv = *max_it;

	return;
}

// for reading a file and writing into a txt-file
// [Str10, p.364]
void fill_vector(istream& istr, vector<int>& v)
{
    int d=0;
    while ( istr >> d) v.push_back(d); // Einlesen
    if (!istr.eof())
    { // Fehlerbehandlung
        cout << " Error handling \n";
        if ( istr.bad() )  throw runtime_error("Schwerer Fehler in istr");
        if ( istr.fail() )   // Versuch des Aufraeumens
        {
            cout << " Failed in reading all data.\n";
            istr.clear();
        }
    }
    v.shrink_to_fit();                 // C++11
    return;
}


void read_vector_from_file(const string& file_name, vector<int>& v)
{
    ifstream fin(file_name);           // Oeffne das File im ASCII-Modus
    if( fin.is_open() )                // File gefunden:
    {
       v.clear();                      // Vektor leeren
       fill_vector(fin, v);
    }
    else                               // File nicht gefunden:
    {
        cout << "\nFile " << file_name << " has not been found.\n\n" ;
        assert( fin.is_open() && "File not found." );       // exeption handling for the poor programmer
    }

 return;
}

void write_vector_to_file(const string& file_name, const vector<float>& v)
{
    ofstream fout(file_name);          // Oeffne das File im ASCII-Modus
    if( fout.is_open() )
    {
       for (unsigned int k=0; k<v.size(); ++k)
       {
          fout << v.at(k) << endl;
       }
    }
    else
    {
        cout << "\nFile " << file_name << " has not been opened.\n\n" ;
        assert( fout.is_open() && "File not opened."  );         // exeption handling for the poor programmer
    }

 return;
}
