#pragma once

#include <algorithm>
#include <iterator>

namespace utils
{
namespace detail
{

template <class T>
concept has_member_reserve = requires(T t) { t.reserve(size_t{}); };

} // namespace detail

template <template <class, class...> class R, class Container, class Functor>
inline auto transform(const Container& container, Functor&& f)
{
    auto result = R<decltype(f(*container.begin()))>{};

    if constexpr (detail::has_member_reserve<decltype(result)>)
    {
        result.reserve(container.size());
    }

    std::transform(container.begin(), container.end(),
                   std::inserter(result, result.end()),
                   std::forward<Functor>(f));

    return result;
}

template <template <class, class...> class Container, class Functor,
          class... Args>
inline auto transform(const Container<Args...>& container, Functor&& f)
{
    return transform<Container, Container<Args...>, Functor>(
        container, std::forward<Functor>(f));
}

} // namespace utils