#include <array>
#include <cassert>
#include <iostream>
#include <functional>
#include <tuple>
#include <type_traits>

/* std::invoke (since C++17) */

#define RETURN(...) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }

template <typename F, typename... As>
auto invoke(F &&f, As &&... as)
    RETURN(std::forward<F>(f)(std::forward<As>(as)...));

template <typename B, typename T, typename D>
auto invoke(T B::*pmv, D &&d) RETURN(std::forward<D>(d).*pmv);

template <typename Pmv, typename Ptr>
auto invoke(Pmv pmv, Ptr &&ptr) RETURN((*std::forward<Ptr>(ptr)).*pmv);

template <typename B, typename T, typename D, typename... As>
auto invoke(T B::*pmf, D &&d, As &&... as)
    RETURN((std::forward<D>(d).*pmf)(std::forward<As>(as)...));

template <typename Pmf, typename Ptr, typename... As>
auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
    RETURN(((*std::forward<Ptr>(ptr)).*pmf)(std::forward<As>(as)...));

#undef RETURN

/* std::apply (since C++17) */

template <typename F, typename Args, size_t... Is>
decltype(auto) apply_impl(F &&f, Args &&args, std::index_sequence<Is...>) {
  return invoke(std::forward<F>(f), std::get<Is>(std::forward<Args>(args))...);
}

template <typename F, typename Args>
decltype(auto) apply(F &&f, Args &&args) {
  return apply_impl(
      std::forward<F>(f),
      std::forward<Args>(args),
      std::make_index_sequence<std::tuple_size<std::decay_t<Args>>::value>());
}

/* function_arity */

template <typename F>
struct function_arity;

template <typename R, typename... Args>
struct function_arity<R (Args...)>
    : std::integral_constant<std::size_t, sizeof...(Args)> {};

template <typename R, typename... Args>
struct function_arity<R (*)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename... Args>
struct function_arity<R (&)(Args...)> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...) const> : function_arity<R (Args...)> {};

template <typename R, typename C, typename... Args>
struct function_arity<R (C::*)(Args...)> : function_arity<R (Args...)> {};

template <typename C>
struct function_arity : function_arity<decltype(&C::operator())> {};

/* make_integer_range */

template <typename T, typename U, T Begin>
struct make_integer_range_impl;

template <typename T, T... Ints, T Begin>
struct make_integer_range_impl<T, std::integer_sequence<T, Ints...>, Begin> {
  using type = std::integer_sequence<T, Begin + Ints...>;
};

template <class T, T Begin, T End>
using make_integer_range =
    typename make_integer_range_impl<T,
                                     std::make_integer_sequence<T, End - Begin>,
                                     Begin>::type;

template <std::size_t Begin, std::size_t End>
using make_index_range = make_integer_range<std::size_t, Begin, End>;

/* slice */

template <std::size_t... Is, std::size_t... Js>
constexpr decltype(auto) slice_impl(std::index_sequence<Is...>,
                                    std::index_sequence<Js...>) {
  using array_t = std::array<std::size_t, sizeof...(Is)>;
  return std::index_sequence<std::get<Js>(array_t{{Is...}})...>();
}

template <std::size_t Begin, std::size_t End, std::size_t... Is>
constexpr decltype(auto) slice(std::index_sequence<Is...> is) {
  return slice_impl(is, make_index_range<Begin, End>());
}

/* partial_sum */

template <typename Is>
struct partial_sum;

template <typename Is>
using partial_sum_t = typename partial_sum<Is>::type;

template <>
struct partial_sum<std::index_sequence<>> {
  using type = std::index_sequence<>;
};

template <std::size_t I, std::size_t... Is>
struct partial_sum<std::index_sequence<I, Is...>> {
  private:

  template <typename Js>
  struct impl;

  template <std::size_t... Js>
  struct impl<std::index_sequence<Js...>> {
    using type = std::index_sequence<I, Js + I...>;
  };

  public:

  using type = typename impl<partial_sum_t<std::index_sequence<Is...>>>::type;

};

/* guarded_apply_impl */

template <typename R>
struct guarded_invoke_impl {
  template <typename F, typename... Args>
  decltype(auto) operator()(F &&f, Args &&...args) const {
    return invoke(std::forward<F>(f), std::forward<Args>(args)...);
  }
};

template <>
struct guarded_invoke_impl<void> {
  template <typename F, typename... Args>
  auto operator()(F &&f, Args &&... args) const {
    invoke(std::forward<F>(f), std::forward<Args>(args)...);
    return nullptr;
  }
};

template <typename T>
struct guarded_invoke_impl<T &> {
  template <typename F, typename... Args>
  auto operator()(F &&f, Args &&...args) const {
    return std::ref(invoke(std::forward<F>(f), std::forward<Args>(args)...));
  }
};

template <typename F, typename... Args>
decltype(auto) guarded_invoke(F &&f, Args &&... args) {
  using R = decltype(invoke(std::forward<F>(f), std::forward<Args>(args)...));
  return guarded_invoke_impl<R>{}(std::forward<F>(f),
                                  std::forward<Args>(args)...);
}

template <typename F, typename Args, size_t... Is>
static decltype(auto) guarded_apply_impl(F &&f,
                                         Args &&args,
                                         std::index_sequence<Is...>) {
  return guarded_invoke(std::forward<F>(f),
                        std::get<Is>(std::forward<Args>(args))...);
}

/* call */

template <typename Fs>
struct call_t {

  explicit call_t(Fs &&fs) : fs_(std::move(fs)) {}

  template <typename... Args>
  auto operator()(Args &&... args) const && {
    using function_arities = typename get_function_arities<Fs>::type;
    auto partial_sum = partial_sum_t<function_arities>{};
    auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
    auto is = slice<0, std::tuple_size<Fs>{}>(partial_sum);
    auto js = slice<1, std::tuple_size<Fs>{} + 1>(partial_sum);
    return apply(
        impl<decltype(tuple), decltype(is), decltype(js)>{std::move(tuple)},
        std::move(fs_));
  }

  private:

  template <typename T>
  struct get_function_arities;

  template <typename... Gs>
  struct get_function_arities<std::tuple<Gs...>> {
    using type = std::index_sequence<0, function_arity<std::decay_t<Gs>>{}...>;
  };

  template <typename Args, typename Is, typename Js>
  struct impl;

  template <typename Args, std::size_t... Is, std::size_t... Js>
  struct impl<Args, std::index_sequence<Is...>, std::index_sequence<Js...>> {

    template <typename... Gs>
    auto operator()(Gs &&... gs) const {
      using tuple_t = decltype(
          std::make_tuple(guarded_apply_impl(std::forward<Gs>(gs),
                                             std::move(args_),
                                             make_index_range<Is, Js>{})...));
      // The use of braced-initializer syntax rather than `make_tuple` is
      // necessary to guarantee left-to-right order of evaluation.
      return tuple_t{guarded_apply_impl(std::forward<Gs>(gs),
                                        std::move(args_),
                                        make_index_range<Is, Js>{})...};
    }

    Args args_;

  };  // impl

  Fs fs_;

};  // call_t

template <typename... Fs>
auto call(Fs &&... fs) {
  auto tuple = std::forward_as_tuple(std::forward<Fs>(fs)...);
  return call_t<decltype(tuple)>{std::move(tuple)};
}

void E() { std::cout << "E()" << std::endl; }

int F(int x, int y) {
  std::cout << "F(" << x << ", " << y << ')' << std::endl;
  return 101;
}

int &G(int &x) {
  std::cout << "G(" << x << ')' << std::endl;
  return x;
}

int main() {
  {
    int n = 42;
    auto result = call(E, F, G)(1, 2, n);
    assert(result == std::make_tuple(nullptr, 101, 42));
  }
  {
    auto e = []() { std::cout << "e()" << std::endl; };
    auto f = [](int x, int y) {
      std::cout << "f(" << x << ", " << y << ')' << std::endl;
      return 101;
    };
    auto g = [](int &x) -> int & {
      std::cout << "g(" << x << ')' << std::endl;
      return x;
    };
    int n = 42;
    auto result = call(e, f, g)(1, 2, n);
    assert(result == std::make_tuple(nullptr, 101, 42));
  }
}