#include <type_traits>
#include <unordered_map>
#include <vector>
#include <queue>
#include <iostream>
#include <map>
#include <memory>
#include <functional>
#include <cmath>
#include <list>
 
#include <boost/iterator/transform_iterator.hpp>
#include <boost/core/demangle.hpp>
 
namespace xstd
{
   template <typename Container>
    struct container_traits
    {
 
    };
 
    template <template <typename, typename> typename ValueContainer, 
        typename Value, 
        typename Allocator>
    struct container_traits<ValueContainer<Value, Allocator>> {
        using container_type = ValueContainer<Value, Allocator>;
        using allocator_type = typename container_type::allocator_type;
        using allocator_traits = std::allocator_traits<allocator_type>;
 
        using value_type = Value;
 
        template <typename U>
        using rebound_allocator_type = typename allocator_traits::template rebind_alloc<U>;
 
        template <typename U>
        using rebound_container_type = ValueContainer<U, rebound_allocator_type<U>>;
    };
 
    template <template <typename, typename, typename, typename> typename KeyValueContainer, 
        typename Key, 
        typename Value, 
        template <typename> typename Less, 
        typename Allocator>
    struct container_traits<KeyValueContainer<Key, Value, Less<Key>, Allocator>> {
        using container_type = KeyValueContainer<Key, Value, Less<Key>, Allocator>;
        using allocator_type = typename container_type::allocator_type;
        using less_type = Less<Key>;
        using allocator_traits = std::allocator_traits<allocator_type>;
 
        using key_type = Key;
        using value_type = Value;
 
        template <typename U>
        using rebound_allocator_type = typename allocator_traits::template rebind_alloc<U>;
 
        template <typename U>
        using rebound_compare_type = Less<U>;
 
        template <typename T, typename U>
        using rebound_kv_container_type = KeyValueContainer<T, U, rebound_compare_type<T>, rebound_allocator_type<std::pair<T, U>>>;
 
        template <typename T, 
            typename U = rebound_allocator_type<T>, 
            template <typename, typename> typename Container = std::vector>
        using rebound_container_type = Container<T, U>;
    };
 
 
    template <template <typename, size_t> typename Container, typename T, size_t N>
    struct container_traits<Container<T, N>>
    {
        using container_type = Container<T, N>;
        using value_type = T;
        constexpr static const size_t arity = N;
 
        template <typename U>
        using rebound_container_type = Container<U, arity>;
    };
 
    template <typename T, 
        typename Container, 
        typename U>
    auto transform(Container const& source, std::function<U(T*)> fn) 
        -> std::enable_if<
            std::is_same<
                typename container_traits<Container>::value_type, 
                typename std::add_pointer<T>::type
            >::value, 
            typename container_traits<Container>::template rebound_container_type<U>
        > {
        auto begin = boost::make_transform_iterator(source.begin(), fn);
        auto end = boost::make_transform_iterator(source.end(), fn);
 
        using target_t = typename container_traits<Container>::template rebound_container_type<U>;
        target_t transformed(begin, end);
        return transformed;
    }
 
    template <typename T, 
        typename Container, 
        typename U>
    auto transform(Container const& source, std::function<U(T const&)> fn) 
        -> typename container_traits<Container>::template rebound_container_type<U> {
        auto begin = boost::make_transform_iterator(source.begin(), fn);
        auto end = boost::make_transform_iterator(source.end(), fn);
 
        using target_t = typename container_traits<Container>::template rebound_container_type<U>;
        target_t transformed(begin, end);
        return transformed;
    }
 
    // Resolves for free-standing function pointer arguments
    template <typename U, typename Container, typename T>
    auto transform(Container const& source, U (*fn)(T)) 
        -> typename container_traits<Container>::template rebound_container_type<U> {
        auto begin = boost::make_transform_iterator(source.begin(), fn);
        auto end = boost::make_transform_iterator(source.end(), fn);
 
        using target_t = typename container_traits<Container>::template rebound_container_type<U>;
        target_t transformed(begin, end);
        return transformed;
    }
 
    template <typename Container, typename Transform>
    auto transform(Container const& source, Transform fn) 
        -> typename container_traits<Container>::template rebound_container_type<
            typename std::result_of<Transform(typename container_traits<Container>::value_type)>::type
        > {
        auto begin = boost::make_transform_iterator(source.begin(), fn);
        auto end = boost::make_transform_iterator(source.end(), fn);
 
        using U = typename std::result_of<Transform(typename container_traits<Container>::value_type)>::type;
 
        using target_t = typename container_traits<Container>::template rebound_container_type<U>;
        target_t transformed(begin, end);
        return transformed;
    }
 
    template <template <typename, size_t> typename Container, typename T, size_t N, typename Transform>
    auto transform(Container<T, N> const& source, Transform fn) 
        -> typename container_traits<Container<T, N>>::template rebound_container_type<
            typename std::result_of<Transform(typename container_traits<Container<T, N>>::value_type)>::type
        > {
 
        using U = typename std::result_of<Transform(typename container_traits<Container<T, N>>::value_type)>::type;
 
        using target_t = typename container_traits<Container<T, N>>::template rebound_container_type<U>;
        target_t transformed;
 
        for (size_t i = 0; i < N; ++i) {
            transformed[i] = fn(source[i]);
        }
 
        return transformed;
    }
}
 
int round(float f) { return std::round(f); }
 
int main()
{
    using vint_trait = xstd::container_traits<std::vector<int>>;
    using ftype = typename vint_trait::template rebound_container_type<float>;
 
    using mintfloat_trait = xstd::container_traits<std::map<int, float>>;
    using miftype = typename mintfloat_trait::template rebound_kv_container_type<float, int>;
 
    using mitype = typename mintfloat_trait::template rebound_container_type<float>;
 
    std::cout << boost::core::demangle(typeid(ftype).name()) << std::endl;
    std::cout << boost::core::demangle(typeid(miftype).name()) << std::endl;
    std::cout << boost::core::demangle(typeid(mitype).name()) << std::endl;
 
    {
        std::vector<int> is { 1, 2, 3, 4, 5 };
        std::vector<float> fs = xstd::transform(is, [](int i) -> float { return float(0.5f + i); });
        for (auto&& itr : fs) {
            std::cout << itr << " ";
        }std::cout << std::endl;
    }
 
    {
        std::array<int, 5> ia { 1, 2, 3, 4, 5 };
        std::array<float, 5> fa = xstd::transform(ia, [](int i) -> float { return float(0.75f + i); });
        for (auto&& itr : fa) {
            std::cout << itr << " ";
        }
        std::cout << std::endl; 
    }
    return 0;
}