#include <iostream>
#include <tuple>
#include <type_traits>

template <typename T> struct Identity { using type = T; };

// Merging packs of types.
template <typename...> struct MergePacks;

template <typename Pack>
struct MergePacks<Pack> : Identity<Pack> {};

template <template <typename...> class P, typename... Types1, typename... Types2, typename... Packs>
struct MergePacks<P<Types1...>, P<Types2...>, Packs...> : MergePacks<P<Types1..., Types2...>, Packs...> {};

// Appending a type to a pack.
template <typename Pack, typename T> struct AppendType;

template <template <typename...> class P, typename... Ts, typename T>
struct AppendType <P<Ts...>, T> {
	using type = P<Ts..., T>;
};

// ExpandPackWithTuple takes a pack, and creates N packs that each end with the tuple's elements, N is the size of the tuple.
template <template <typename...> class P, typename Pack, typename Tuple, typename Indices> struct ExpandPackWithTupleHelper;

template <template <typename...> class P, typename Pack, typename Tuple, std::size_t... Is>
struct ExpandPackWithTupleHelper<P, Pack, Tuple, std::index_sequence<Is...>> {
	using type = P<typename AppendType<Pack, typename std::tuple_element<Is, Tuple>::type>::type...>;
};

template <template <typename...> class P, typename Pack, typename Tuple>
using ExpandPackWithTuple = typename ExpandPackWithTupleHelper<P, Pack, Tuple, std::make_index_sequence<std::tuple_size<Tuple>::value>>::type;

// Now, for TupleTree itself.
template <template <typename...> class P, typename OutputPack, typename... Tuples> struct TupleTreeHelper;

template <template <typename...> class P, typename... Ts, typename First, typename... Rest>
struct TupleTreeHelper<P, P<Ts...>, First, Rest...> : TupleTreeHelper<P, typename MergePacks<ExpandPackWithTuple<P, Ts, First>...>::type, Rest...> {};

template <template <typename...> class P, typename OutputPack>
struct TupleTreeHelper<P, OutputPack> {
	using type = OutputPack;
};

template <template <typename...> class P, typename... Tuples>
using TupleTree = typename TupleTreeHelper<P, P<P<>>, Tuples...>::type;

// Testing
template <typename...> struct Pack;
using T1 = std::tuple<int, char, double>;
using T2 = std::tuple<bool, double, int, char>;

int main() {
	std::cout << std::boolalpha << std::is_same<
		TupleTree<Pack, T1, T2>,
		Pack<
			Pack<int, bool>, Pack<int, double>, Pack<int, int>, Pack<int, char>,
			Pack<char, bool>, Pack<char, double>, Pack<char, int>, Pack<char, char>,
			Pack<double, bool>, Pack<double, double>, Pack<double, int>, Pack<double, char>
		>
	>::value << '\n';  // true
}
