#ifndef __GBLIBCPP_BITS_TUPLE_TOOLS__
#define __GBLIBCPP_BITS_TUPLE_TOOLS__

#include <cstddef>

#include <bits/type_traits/remove_add>

#include <bits/forward>
#include <bits/sequence>

#include <bits/fwd_functional>

namespace std {

template <typename... Types>
class tuple;

template <std::size_t I, typename T>
struct tuple_element;

template <std::size_t I, typename T>
using tuple_element_t = typename tuple_element<I, T>::type;

template <typename T>
struct tuple_size;

template <typename T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <std::size_t I, typename... Types>
constexpr auto get(std::tuple<Types...>& tpl) noexcept
    -> tuple_element_t<I, std::tuple<Types...>>&
{ return tpl.template _getl<I>(); }

template <std::size_t I, typename... Types>
constexpr auto get(std::tuple<Types...>&& tpl) noexcept
    -> tuple_element_t<I, std::tuple<Types...>>&&
{ return tpl.template _getr<I>(); }

template <std::size_t I, typename... Types>
constexpr auto get(const std::tuple<Types...>& tpl) noexcept
    -> tuple_element_t<I, std::tuple<Types...>> const&
{ return tpl.template _getl<I>(); }

template <std::size_t I, typename... Types>
constexpr auto get(const std::tuple<Types...>&& tpl) noexcept
    -> tuple_element_t<I, std::tuple<Types...>> const&&
{ return tpl.template _getr<I>(); }

namespace __helpers {

template <typename T, typename Tuple, std::size_t... I>
constexpr T make_from_tuple_impl(Tuple&& tpl, std::index_sequence<I...>)
{
    return T(std::get<I>(std::forward<Tuple>(tpl))...);
}

template <typename _T, typename T = std::decay_t<_T>>
struct __to_tuple_type { using type = T; };

template <typename _T, typename T>
struct __to_tuple_type<_T, std::reference_wrapper<T>>
{ using type = std::add_lvalue_reference_t<T>; };

template <typename T>
using to_tuple_type_t = typename __to_tuple_type<T>::type;

} // namespace __helpers

template <typename T, typename Tuple>
constexpr T make_from_tuple(Tuple&& tpl)
{
    return __helpers::make_from_tuple_impl<T>(
        std::forward<Tuple>(tpl),
        std::make_index_sequence<
            std::tuple_size_v<std::remove_cvref_t<Tuple>>> {}
    );
}

}

#endif
