// IsPermutation<Pack1, Pack2>::value has value true if the types in Pack1 are a permutation of the types in Pack2.

#include <iostream>
#include <type_traits>

template <typename, typename> struct ExistsInPack;

template <typename T, template <typename...> class P>
struct ExistsInPack<T, P<>> : std::false_type {};

template <typename T, template <typename...> class P, typename... Rest>
struct ExistsInPack<T, P<T, Rest...>> : std::true_type {};

template <typename T, template <typename...> class P, typename First, typename... Rest>
struct ExistsInPack<T, P<First, Rest...>> : ExistsInPack<T, P<Rest...>> {};

template <std::size_t N, typename T, typename Pack> struct CountHelper;

template <std::size_t N, typename T, template <typename...> class P>
struct CountHelper<N, T, P<>> : std::integral_constant<std::size_t, N> {};

template <std::size_t N, typename T, template <typename...> class P, typename... Rest>
struct CountHelper<N, T, P<T, Rest...>> : CountHelper<N+1, T, P<Rest...>> {};

template <std::size_t N, typename T, template <typename...> class P, typename First, typename... Rest>
struct CountHelper<N, T, P<First, Rest...>> : CountHelper<N, T, P<Rest...>> {};

template <typename T, typename Pack>
using Count = CountHelper<0, T, Pack>;  // Count<T, Pack>::value is the number of occurrences of T in Pack.

template <typename Pack1, typename Pack2, typename AlreadyCounted> struct CountFirstType;

template <template <typename...> class P, typename Pack, typename AlreadyCounted>
struct CountFirstType<P<>, Pack, AlreadyCounted> : std::true_type {};

template <template <typename...> class P, typename First, typename... Rest, typename Pack, typename... AlreadyCounted>
struct CountFirstType<P<First, Rest...>, Pack, P<AlreadyCounted...>> : std::conditional_t<
	ExistsInPack<First, P<AlreadyCounted...>>::value,
	CountFirstType<P<Rest...>, Pack, P<AlreadyCounted...>>,
	std::conditional_t<
		Count<First, P<First, Rest...>>::value == Count<First, Pack>::value,
		CountFirstType<P<Rest...>, Pack, P<AlreadyCounted..., First>>,
		std::false_type
	>
> {};

template <typename Pack1, typename Pack2> struct IsPermutation;

template <template <typename...> class P, typename... Ts, typename... Us>
struct IsPermutation<P<Ts...>, P<Us...>> : CountFirstType<P<Ts...>, P<Us...>, P<>> {};

template <template <typename...> class P, typename First, typename... Rest1, typename... Rest2>
struct IsPermutation<P<First, Rest1...>, P<First, Rest2...>> : IsPermutation<P<Rest1...>, P<Rest2...>> {};

template <template <typename...> class P>
struct IsPermutation<P<>, P<>> : std::true_type {};

template <template <typename...> class P, typename... Ts>
struct IsPermutation<P<>, P<Ts...>> : std::false_type {};

template <template <typename...> class P, typename... Ts>
struct IsPermutation<P<Ts...>, P<>> : std::false_type {};

// Testing
template <typename...> struct P;

int main() {
	std::cout << std::boolalpha << IsPermutation< P<int, char, bool>, P<int, char, bool> >::value << '\n';  // true
	std::cout << IsPermutation< P<int, char, bool>, P<int, char, bool, int> >::value << '\n';  // false
	std::cout << IsPermutation< P<int, char, bool, int>, P<int, char, bool> >::value << '\n';  // false
	std::cout << IsPermutation< P<int, char, bool, long>, P<int, char, long, bool> >::value << '\n';  // true
	std::cout << IsPermutation< P<int, char, bool, long, long, bool, long>, P<int, char, long, bool, long, long, bool> >::value << '\n';  // true
	std::cout << IsPermutation< P<int, char, bool, long>, P<int, char, long, bool, long> >::value << '\n';  // false
}
