#pragma once
#ifndef COMMON_H
#define COMMON_H


#include <set>
#include <vector>
#include <cassert>
#include <numeric>
#include <limits>
#include <iterator>
#include <algorithm>
#include <map>
#include <string>
#include <chrono>

using std::string;
using std::vector;

//XML floats (1.2 will be written as 1200)
#define STR_DOUBLE_PRECISION 1000

namespace common {

    //XML helpers
    string float_to_str(double v);
    double str_to_float(const string& s);
    string vec_to_str(const vector<string>& vec, char sep = ';');
    string vec_to_str(const vector<double>& vec, char sep = ';');
    string vec_to_str(const vector<size_t>& vec, char sep = ';');
    string vec_to_str(const vector<bool>& vec, char sep = ';');
    vector<double> str_to_vec_double(const string& str, char sep = ';');
    vector<bool> str_to_vec_bool(const string& str, char sep = ';');
    vector<unsigned int> str_to_vec_uint(const string& str, char sep);

    string bool_to_str(bool val);
    bool str_to_bool(const string& str);
    std::vector<std::string> split(const std::string &s, char delim);

    //stats helpers
    template<typename T = double>
    T mean(vector<T> vec) {
        T sum = 0;
        for(T v : vec) sum += v;
        return sum / vec.size();
    }

	//standard deviation
    template<typename T = double>
    T std(vector<T> vec) {
        T vmean = mean(vec);
        T sum = 0;
        for(T v : vec)
            sum += (v - vmean) * (v - vmean);
        return std::sqrt(sum / vec.size());
    }

    template<typename T>
    T factorial(T n) {
        return (n < 2) ? 1 : factorial(n - 1) * n;
    }

    template<class Lambda>
    long long benchmark(Lambda func){
	    auto start = std::chrono::steady_clock::now();
	    func();
	    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start);
	    return duration.count();
    }

    //Combinations with repetions
    //based on 
    //https://gist.github.com/rg3/731629
    template<class T>
    void combinations_with_rep_recursive(const vector<T> &elems, unsigned long req_len,
                                         vector<unsigned long> &pos, unsigned long depth,
                                         unsigned long margin, vector<vector<T>>& output, size_t& idx) {
        // Have we selected the number of required elements?
        if(depth >= req_len) {
            output.push_back(vector<T>());
            for(unsigned long ii = 0; ii < pos.size(); ++ii)
                output[idx].push_back(elems[pos[ii]]);
            idx++;
            return;
        }

        // Try to select new elements to the right of the last selected one.
        for(unsigned long ii = margin; ii < elems.size(); ++ii) {
            pos[depth] = ii;
            combinations_with_rep_recursive(elems, req_len, pos, depth + 1, ii, output, idx);
        }
        return;
    }

    template<class T>
    vector<vector<T>> combinations_with_rep(const vector<T> &elems, unsigned long req_len) {
        vector<vector<T>> res;
        if(req_len == 0)
            return res;
        assert(req_len <= elems.size());
        vector<unsigned long> positions(req_len, 0);
        size_t idx = 0;
        combinations_with_rep_recursive(elems, req_len, positions, 0, 0, res, idx);
        return res;
    }

    template<typename T>
    T find_most_frequent(const vector<T> &vect) {
        //key - vector item, value - frequency 
        std::map<T, std::size_t> dict;
        for(size_t i = 0; i < vect.size(); i++)
            dict[vect[i]] = 0;
        for(size_t i = 0; i < vect.size(); i++)
            dict[vect[i]] += 1;

        T result = 0;
        size_t  max = 0; // size_t  is std::size_t, so only positive values!
        for(typename std::map<T, std::size_t>::iterator it = dict.begin(); it != dict.end(); it++) {
            if(max < it->second) {
                max = it->second;
                result = it->first;
            }
        }
        return result;
    }

    template<typename T>
    vector<std::size_t> arg_ksmallest(const vector<T> &vect, const size_t  k) {
        if(k < 10) return arg_ksmallest_v2(vect, k);
        else return arg_ksmallest_v1(vect, k);

    }

	//return indices of k smallest elements
    template<typename T>
    vector<size_t> arg_ksmallest_v1(const vector<T> &vect, const size_t  k) {
        size_t k_ = k < vect.size() ? k : vect.size();
        vector<std::pair<T, size_t>> v(vect.size());
        for(size_t i = 0; i < vect.size(); i++)
            v[i] = std::make_pair(vect[i], i);
        std::partial_sort(v.begin(), v.begin() + k_, v.end(),              
            [](const std::pair<T, size_t>& l, const std::pair<T, size_t>& r) {
            return l.first < r.first;
        });

        vector<size_t> res(k_);
        for(size_t i = 0; i < k_ ; i++)
            res[i] = v[i].second;
        return res;
    }

	//return indices of k smallest elements, faster than v1 for small k (and large vect)
    template<typename T> 
    vector<size_t> arg_ksmallest_v2(const vector<T> &vect, const size_t  k){
        vector<size_t> indices;
        std::set<size_t> found;
        for(size_t i = 0; i < k; i++) {
            size_t  cur_idx = 0;
            T temp_min = std::numeric_limits<T>::max();
            for(size_t j = 0; j < vect.size(); j++) {
                if(temp_min > vect[j] && (found.find(j) == found.end())) {
                    temp_min = vect[j];
                    cur_idx = j;
                }
            }
            indices.push_back(cur_idx);
            found.insert(cur_idx);
        }
        return indices;
    }



}

#endif
