/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include 
#include 
#include 
#include 
namespace td {
namespace detail {
template 
struct IntSeq {};
template 
struct IntSeqGen : IntSeqGen {};
template 
struct IntSeqGen {
  using type = IntSeq;
};
template 
class LogicAndImpl {};
template 
class LogicAndImpl {
 public:
  static constexpr bool value = LogicAndImpl<(Res && X), Args...>::value;
};
template 
class LogicAndImpl {
 public:
  static constexpr bool value = Res;
};
template 
using IntRange = typename IntSeqGen<0, N>::type;
template 
struct is_reference_wrapper : std::false_type {};
template 
struct is_reference_wrapper> : std::true_type {};
template 
auto invoke_impl(T Base::*pmf, Derived &&ref,
                 Args &&... args) noexcept(noexcept((std::forward(ref).*pmf)(std::forward(args)...)))
    -> std::enable_if_t::value && std::is_base_of>::value,
                        decltype((std::forward(ref).*pmf)(std::forward(args)...))> {
  return (std::forward(ref).*pmf)(std::forward(args)...);
}
template 
auto invoke_impl(T Base::*pmf, RefWrap &&ref,
                 Args &&... args) noexcept(noexcept((ref.get().*pmf)(std::forward(args)...)))
    -> std::enable_if_t::value && is_reference_wrapper>::value,
                        decltype((ref.get().*pmf)(std::forward(args)...))>
{
  return (ref.get().*pmf)(std::forward(args)...);
}
template 
auto invoke_impl(T Base::*pmf, Pointer &&ptr,
                 Args &&... args) noexcept(noexcept(((*std::forward(ptr)).*pmf)(std::forward(args)...)))
    -> std::enable_if_t::value && !is_reference_wrapper>::value &&
                            !std::is_base_of>::value,
                        decltype(((*std::forward(ptr)).*pmf)(std::forward(args)...))> {
  return ((*std::forward(ptr)).*pmf)(std::forward(args)...);
}
template 
auto invoke_impl(T Base::*pmd, Derived &&ref) noexcept(noexcept(std::forward(ref).*pmd))
    -> std::enable_if_t::value && std::is_base_of>::value,
                        decltype(std::forward(ref).*pmd)> {
  return std::forward(ref).*pmd;
}
template 
auto invoke_impl(T Base::*pmd, RefWrap &&ref) noexcept(noexcept(ref.get().*pmd))
    -> std::enable_if_t::value && is_reference_wrapper>::value,
                        decltype(ref.get().*pmd)> {
  return ref.get().*pmd;
}
template 
auto invoke_impl(T Base::*pmd, Pointer &&ptr) noexcept(noexcept((*std::forward(ptr)).*pmd))
    -> std::enable_if_t::value && !is_reference_wrapper>::value &&
                            !std::is_base_of>::value,
                        decltype((*std::forward(ptr)).*pmd)> {
  return (*std::forward(ptr)).*pmd;
}
template 
auto invoke_impl(F &&f, Args &&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...)))
    -> std::enable_if_t>::value,
                        decltype(std::forward(f)(std::forward(args)...))> {
  return std::forward(f)(std::forward(args)...);
}
template 
auto invoke(F &&f,
            ArgTypes &&... args) noexcept(noexcept(invoke_impl(std::forward(f), std::forward(args)...)))
    -> decltype(invoke_impl(std::forward(f), std::forward(args)...)) {
  return invoke_impl(std::forward(f), std::forward(args)...);
}
template 
auto call_tuple_impl(F &&func, std::tuple &&tuple, IntSeq) {
  return func(std::forward(std::get(tuple))...);
}
template 
auto invoke_tuple_impl(std::tuple &&tuple, IntSeq) {
  return invoke(std::forward(std::get(tuple))...);
}
template 
auto mem_call_tuple_impl(ActorT *actor, std::tuple &&tuple, IntSeq<0, S...>) {
  return (actor->*std::get<0>(tuple))(std::forward(std::get(tuple))...);
}
template 
void tuple_for_each_impl(std::tuple &tuple, const F &func, IntSeq) {
  const auto &dummy = {0, (func(std::get(tuple)), 0)...};
  (void)dummy;
}
template 
void tuple_for_each_impl(const std::tuple &tuple, const F &func, IntSeq) {
  const auto &dummy = {0, (func(std::get(tuple)), 0)...};
  (void)dummy;
}
}  // namespace detail
template 
class LogicAnd {
 public:
  static constexpr bool value = detail::LogicAndImpl::value;
};
template 
auto call_tuple(F &&func, std::tuple &&tuple) {
  return detail::call_tuple_impl(func, std::move(tuple), detail::IntRange());
}
template 
auto invoke_tuple(std::tuple &&tuple) {
  return detail::invoke_tuple_impl(std::move(tuple), detail::IntRange());
}
template 
auto mem_call_tuple(ActorT *actor, std::tuple &&tuple) {
  return detail::mem_call_tuple_impl(actor, std::move(tuple), detail::IntRange());
}
template 
void tuple_for_each(std::tuple &tuple, const F &func) {
  detail::tuple_for_each_impl(tuple, func, detail::IntRange());
}
template 
void tuple_for_each(const std::tuple &tuple, const F &func) {
  detail::tuple_for_each_impl(tuple, func, detail::IntRange());
}
template  = 0>
auto &&get_nth_argument(Arg &&arg, Args &&... args) {
  return std::forward(arg);
}
template  = 0>
auto &&get_nth_argument(Arg &&arg, Args &&... args) {
  return get_nth_argument(std::forward(args)...);
}
template 
auto &&get_last_argument(Args &&... args) {
  return get_nth_argument(std::forward(args)...);
}
namespace detail {
template 
auto call_n_arguments_impl(IntSeq, F &&f, Args &&... args) {
  return f(get_nth_argument(std::forward(args)...)...);
}
}  // namespace detail
template 
auto call_n_arguments(F &&f, Args &&... args) {
  return detail::call_n_arguments_impl(detail::IntRange(), f, std::forward(args)...);
}
template 
struct is_callable : public std::false_type {};
template 
struct is_callable()(std::declval()))> : public std::true_type {};
}  // namespace td