#pragma once

#include <array>
#include <functional>

template<typename T>
concept LinearArray =  requires(typename T::value_type& lhs, const typename T::value_type& rhs){
    std::is_same_v<std::array<typename T::value_type, std::tuple_size<T>::value>, T>;// 只对array进行操作
    lhs+=rhs;
    lhs-=rhs;
    std::is_copy_constructible<T>::value;
};

template<LinearArray T>
inline T& operator+=(T& lhs, const T& rhs) {
    for(std::size_t i = 0; i < std::tuple_size<T>::value; ++i){
        lhs[i] += rhs[i];
    }
    return lhs;
}

template<LinearArray T>
inline T operator+(const T& lhs, const T& rhs){
    T ret{lhs};
    add(ret, rhs);
    return ret;
}

template<LinearArray T>
inline T& operator-=(T& lhs, const T& rhs) {
    for(int i = 0; i < std::tuple_size<T>::value; ++i){
        lhs[i] -= rhs[i];
    }
    return lhs;
} 

template<LinearArray T>
inline T operator-(const T& lhs, const T& rhs){
    T ret{lhs};
    subtract(ret, rhs);
    return ret;
}

template <typename T>
requires requires(const T& t){
    { std::hash<typename T::value_type>{}(*static_cast<typename T::value_type*>(nullptr)) } -> std::convertible_to<std::size_t>;
} && ( requires (const T& t){t.end(); t.begin();} || requires (const T& t){std::end(t);std::begin(t);} )
struct ContainerHash
{
    std::size_t operator()(const T& arr) const
    {
        std::size_t seed = 0;
        for(auto iter=arr.begin(); iter!=arr.end(); ++iter){
            hash_combine(seed, *iter);
        }

        return seed;
    }
private:
    std::hash<typename T::value_type> hashfun = std::hash<typename T::value_type>{};
    void hash_combine(std::size_t& seed, const typename T::value_type& v) const
    {
        seed ^= hashfun(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
    }
};

using ShowdownHistogram = std::array<int64_t, 3>;
using ShowdownHistogram2 = std::array<int64_t, 2>;

inline ShowdownHistogram2 to_showdown_histogram_2(const ShowdownHistogram& hs){
    ShowdownHistogram2 ret{2*hs[0] + hs[1], 2*hs[2] + hs[1]};
    return ret;
}

