
#ifndef Utiles_h
#define Utiles_h

#include <assert.h>

#include <algorithm>
#include <cctype>
#include <functional>
#include <map>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <vector>
// #include "log.h"

namespace jf {
template <typename ValueType>
inline std::string to_string(const ValueType &in) {
    return std::to_string(in);
}
inline std::string to_string(const std::string &in) { return in; }
inline std::string to_string(const char *in) { return in; }
template <typename ElemType>
inline std::string to_string(const std::vector<ElemType> &in) {
    return std::accumulate(in.begin(), in.end(), std::string(""), [](const std::string &acc, const ElemType &e) { return acc + std::to_string(e); });
}
template <typename KeyType, typename ValueType>
inline std::string to_string(const std::map<KeyType, ValueType> &m) {
    return std::accumulate(m.begin(), m.end(), std::string(""), [](const std::string &acc, const std::pair<KeyType, ValueType> &e) { return acc + ToString(e.first) + ":" + ToString(e.second) + ", "; });
}

template <typename ValueType>
inline void from_string(const std::string &in, ValueType &out) {
    // jf_log_error("assert false : from_string");
    return out = ValueType{};
}
template <>
inline void from_string(const std::string &in, float &out) {
    out = atof(in.c_str());
}
template <>
inline void from_string(const std::string &in, int &out) {
    out = atoi(in.c_str());
}
template <>
inline void from_string(const std::string &in, std::string &out) {
    out = in;
}
template <>
inline void from_string(const std::string &in, double &out) {
    out = atof(in.c_str());
}

template <typename retType>
inline retType from_string(const std::string &in) {
    return atof(in.c_str());
}
template <>
inline float from_string(const std::string &in) {
    return atof(in.c_str());
}
template <>
inline int from_string(const std::string &in) {
    return atoi(in.c_str());
}
template <>
inline std::string from_string(const std::string &in) {
    return in;
}
template <>
inline double from_string(const std::string &in) {
    return atof(in.c_str());
}

namespace Utils {
template <typename KeyType, typename ValueType>
const ValueType *MapValue(const std::map<KeyType, const ValueType *> &m, const KeyType &k) {
    auto i = m.find(k);
    if (i == m.end()) {
        return nullptr;
    } else {
        return i->second;
    }
}
template <typename KeyType, typename ValueType>
ValueType *MapValue(const std::map<KeyType, ValueType *> &m, const KeyType &k) {
    auto i = m.find(k);
    if (i == m.end()) {
        return nullptr;
    } else {
        return i->second;
    }
}

template <typename KeyType, typename ValueType>
ValueType MapValue(const std::map<KeyType, ValueType> &m, const KeyType &k) {
    auto i = m.find(k);
    if (i == m.end()) {
        return ValueType();
    } else {
        return i->second;
    }
}

template <typename KeyType, typename ValueType>
bool MapValue(const std::map<KeyType, ValueType> &m, const KeyType &k, ValueType &v) {
    auto i = m.find(k);
    if (i == m.end()) {
        return false;
    } else {
        v = i->second;
        return true;
    }
}

template <class ValueType>
ValueType StringToNum(const std::string &str) {
    std::istringstream iss(str);
    ValueType num;
    iss >> num;
    return num;
}

inline std::string LTrim(std::string &str) {
    int len = str.length();
    int i = 0;

    while (i < len && isspace(str[i])) {
        i++;
    }
    if (i == len) {
        return std::string("");
    }

    str = str.substr(i, len);

    return str;
}

inline std::string RTrim(std::string &str) {
    int i = str.length();

    while (i >= 0 && isspace(str[i])) {
        i--;
    }
    if (i == -1) {
        return std::string("");
    }

    str = str.substr(0, i);

    return str;
}

inline std::string Trim(std::string &str) {
    std::string ret = RTrim(str);
    ret = LTrim(ret);
    return ret;
}

inline std::vector<std::string> Split(const std::string &s, const char flag = ' ') {
    std::vector<std::string> ret;
    std::istringstream iss(s);
    std::string temp;

    while (getline(iss, temp, flag)) {
        ret.push_back(Trim(temp));
    }
    return ret;
}

template <typename eT>
std::vector<eT> Set2Vector(const std::set<eT> &s) {
    return std::vector<eT>(s.begin(), s.end());
}

template <typename kT, typename vT>
std::vector<kT> Map2Vector(const std::map<kT, vT> &m) {
    std::vector<kT> ret;
    for (auto &iter : m) {
        auto &k = iter.first;
        auto &v = iter.second;
        ret.push_back(k);
    }
    return ret;
}

template <typename eT, typename linqType>
void LinqAssign(std::vector<eT> &target, linqType &&q) {
    target.assign(q.begin(), q.end());
}

inline int DoubleToInt(double in) {
    int i = int(in);
    if (in - i > 0.999999) {
        return int(in + 1);
    }
    return i;
}

template <typename eT>
inline std::set<eT> SetUnion(const std::set<eT> &l, const std::set<eT> &r) {
    const std::set<eT> *pl = &l;
    const std::set<eT> *pr = &r;
    const std::set<eT> *less;
    const std::set<eT> *more;
    if (l.size() < r.size()) {
        less = pl;
        more = pr;
    } else {
        less = pr;
        more = pl;
    }
    std::set<eT> ret = *more;
    for (const eT &e : *less) {
        ret.insert(e);
    }
    return ret;
}

template <typename ElemType>
inline std::set<ElemType> Vector2Set(const std::vector<ElemType> &in) {
    return std::set<ElemType>(in.begin(), in.end());
}

template <typename ValueType>
bool VectorContains(const std::vector<ValueType> &vec, const ValueType &v) {
    auto it = find(vec.begin(), vec.end(), v);
    return it != vec.end();
}

template <typename ElemType>
inline bool SetContains(const std::set<ElemType> &in, const ElemType &e) {
    return in.find(e) != in.end();
    ;
}
template <typename ElemType>
bool Contains(const std::vector<ElemType> &in, const ElemType &k) {
    return std::find(in.begin(), in.end(), k) != in.end();
}
template <typename ElemType>
bool Contains(const std::set<ElemType> &in, const ElemType &k) {
    return std::find(in.begin(), in.end(), k) != in.end();
}
template <typename KeyType, typename ValueType>
bool Contains(const std::map<KeyType, ValueType> &in, const KeyType &k) {
    return std::find_if(in.begin(), in.end(), [&](const std::pair<KeyType, ValueType> &p) { return p.first == k; }) != in.end();
}

template <typename ElemType>
std::vector<ElemType> ContainerUnion(const std::vector<ElemType> &l, const std::vector<ElemType> &r) {
    std::vector<ElemType> ret = l;
    ret.insert(ret.end(), r.begin(), r.end());
    return ret;
}
template <typename ElemType>
std::set<ElemType> ContainerUnion(const std::set<ElemType> &l, const std::set<ElemType> &r) {
    return SetUnion(l, r);
}
inline bool StartsWith(const std::string &big, const std::string &small) {
    if (&big == &small) return true;
    const typename std::string::size_type big_size = big.size();
    const typename std::string::size_type small_size = small.size();
    const bool valid_ = (big_size >= small_size);
    const bool starts_with_ = (big.compare(0, small_size, small) == 0);
    return valid_ and starts_with_;
}

inline bool EndsWith(const std::string &big, const std::string &small) {
    if (&big == &small) return true;
    const typename std::string::size_type big_size = big.size();
    const typename std::string::size_type small_size = small.size();
    const bool valid_ = (big_size >= small_size);
    const bool ends_with_ = (big.compare(big_size - small_size, small_size, small) == 0);
    return valid_ and ends_with_;
}

template <typename KeyType, typename ValueType>
inline std::set<KeyType> MapKey2Set(const std::map<KeyType, ValueType> &m) {
    std::set<KeyType> ret;
    for (auto &iter : m) {
        auto &k = iter.first;
        auto &v = iter.second;
        ret.insert(k);
    }
    return ret;
}

template <typename KeyType, typename ValueType>
inline std::set<ValueType> &&MapValue2Set(const std::map<KeyType, ValueType> &m) {
    std::set<ValueType> ret;
    for (auto &iter : m) {
        auto &k = iter.first;
        auto &v = iter.second;
        ret.insert(v);
    }
    return std::move(ret);
}

template <typename ArrayElemType>
void SplitToArray(const std::string &in, const char separator, std::vector<ArrayElemType> &out) {
    std::istringstream ss(in);
    std::string token;

    std::vector<std::string> playerInfoVector;
    while (std::getline(ss, token, separator)) {
        ArrayElemType elem;
        from_string(token, elem);
        out.push_back(elem);
    }
}

template <typename ElemType, typename PredType>
void EraseIf(std::set<ElemType> &s, const PredType &pred) {
    auto first = s.begin();
    auto last = s.end();
    while (first != last) {
        if (pred(*first)) {
            first = s.erase(first);
        } else
            first++;
    }
}

}  // namespace Utils
}  // namespace jf

#endif
