#include <type_traits>
#include <utility>
namespace utilities {
template <typename Pack1, typename Pack2> struct merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
}
template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;
template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
using type = P<P<Output...>>; // Just one single pack (which must be wrapped in P so that the resulting merge
// will gavegive all such single packs, rather than a merge of all the types).
using type = P<P<Output...>>;
};
template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
using type = P<>; // No pack can come out of this (permuting R types from nothing).
using type = P<>;
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
using type = P<>; // No pack can come out of this (since there are lessfewer types in
// P<TypesIgnored..., Rest...> than R).
using type = P<>;
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type, // Case 1: 'First' is in the permuted pack (note that Output..., First are the
// types in just one pack). Now continue to get R-1 more types from
// TypesIgnored..., Rest... (which are the remaining available types since
// 'First' is no longer available for the remaining R-1 types, and the ignored
// types are now P<> since we are starting a new nPr_h call).
typename nPr_h<R-1, P<RestP<TypesIgnored...>, P<TypesIgnoredRest...>, First>P<>, P<Output...>>, First>>::type,
// Case 2: 'First'First' in not in the permuted pack, so try to get R types from
// Rest... to append to Output... First is appended oto TypesIgnored... since
// it is now among those types not used.
typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type
> { };
template <std::size_t R, typename Pack> struct nPr;
template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };
// Testing
template <typename...> struct P;
int main() {
static_assert(std::is_same<
nPr<2, P<int, char, float>>::type,
P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
>::value);
static_assert(std::is_same<
nPr<3, P<int, char, float, bool>>::type,
P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
>::value);
}
#include <type_traits>
#include <utility>
namespace utilities {
template <typename Pack1, typename Pack2> struct merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
}
template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;
template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
using type = P<P<Output...>>; // Just one single pack (which must be wrapped in P so that the resulting merge will gave all such single packs, rather than a merge of all the types).
};
template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
using type = P<>; // No pack can come out of this (permuting R types from nothing).
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
using type = P<>; // No pack can come out of this (since there are less types in P<TypesIgnored..., Rest...> than R).
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type, // Case 1: 'First' is in the permuted pack (note that Output..., First are the types in just one pack). Now continue to get R-1 more types from TypesIgnored..., Rest... (which are the remaining available types since 'First' is no longer available for the remaining R-1 types, and the ignored types are now P<> since we are starting a new nPr_h call).
typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type // Case 2: 'First in not in the permuted pack, so try to get R types from Rest... to append to Output... First is appended o TypesIgnored... since it is now among those types not used.
> { };
template <std::size_t R, typename Pack> struct nPr;
template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };
// Testing
template <typename...> struct P;
int main() {
static_assert(std::is_same<
nPr<2, P<int, char, float>>::type,
P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
>::value);
static_assert(std::is_same<
nPr<3, P<int, char, float, bool>>::type,
P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
>::value);
}
#include <type_traits>
#include <utility>
namespace utilities {
template <typename Pack1, typename Pack2> struct merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
}
template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;
template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
// Just one single pack (which must be wrapped in P so that the resulting merge
// will give all such single packs, rather than a merge of all the types).
using type = P<P<Output...>>;
};
template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
// No pack can come out of this (permuting R types from nothing).
using type = P<>;
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
// No pack can come out of this (since there are fewer types in
// P<TypesIgnored..., Rest...> than R).
using type = P<>;
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
// Case 1: 'First' is in the permuted pack (note that Output..., First are the
// types in just one pack). Now continue to get R-1 more types from
// TypesIgnored..., Rest... (which are the remaining available types since
// 'First' is no longer available for the remaining R-1 types, and the ignored
// types are now P<> since we are starting a new nPr_h call).
typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type,
// Case 2: 'First' in not in the permuted pack, so try to get R types from
// Rest... to append to Output... First is appended to TypesIgnored... since
// it is now among those types not used.
typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type
> { };
template <std::size_t R, typename Pack> struct nPr;
template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };
// Testing
template <typename...> struct P;
int main() {
static_assert(std::is_same<
nPr<2, P<int, char, float>>::type,
P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
>::value);
static_assert(std::is_same<
nPr<3, P<int, char, float, bool>>::type,
P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
>::value);
}
I think I've found a better solution. It also generalizes to Permutation(N,R), with Permutation(N,N) being a special case that answers the original problem. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.
#include <iostream>
#include <type_traits>
template <typename, typename> struct#include Merge;<utility>
template <template <typename...> class P, typename... Ts,namespace typename...utilities Us>{
struct Merge<P<Ts...>, P<Us...>> {
template <typename usingPack1, typetypename =Pack2> P<Ts...,struct Us...>;
};merge;
template <std::size_t<template N,<typename...> typenameclass PackP, typename... PreviousTs, typename... Output>Us>
struct PermutationNHelper;merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
}
template <std::size_t N, template <typename...> class P, typename FirstR, typename... Rest, typename... PrevPack, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>TypesIgnored, Output...> : Merge<
typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type, // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First. The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is= Prev...,void> First.
>struct {};nPr_h;
template <std::size_t N, template<template <typename...> class P, typename PreviousFirst, typename... Rest, typename... TypesIgnored, typename... Output>
struct PermutationNHelper<NnPr_h<0, P<>P<First, PreviousRest...>, OutputP<TypesIgnored...>, P<Output...>> {
using type = P<>;P<P<Output...>>; // Just one single pack (which must be wrapped in P so that the resulting merge will gave all such single packs, rather than a merge of all the types).
};
template <template<std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... PrevTypesIgnored, typename... Output>
struct PermutationNHelper<0, P<FirstnPr_h<R, Rest...>P<>, P<Prev...>TypesIgnored, Output...>Output> { // P<Prev...> must be used (instead of simply 'Previous'), else there will be ambiguity.
using type = P<P<Output...>>;P<>; // Note that itNo mustpack becan P<P<Output...>>come insteadout of P<Output...> for thethis merging(permuting toR worktypes asfrom intendednothing).
};
template <template<std::size_t R, template <typename...> class P, typename PreviousFirst, typename... Rest, typename... TypesIgnored, typename... Output>
struct PermutationNHelper<0nPr_h<R, P<>P<First, PreviousRest...>, OutputP<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
using type = P<P<OutputP<>; // No pack can come out of this (since there are less types in P<TypesIgnored...>>;, Rest...> than R).
};
template <std::size_t R, template <typename...> Pack>class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct EmptyPack;nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type, // Case 1: 'First' is in the permuted pack (note that Output..., First are the types in just one pack). Now continue to get R-1 more types from TypesIgnored..., Rest... (which are the remaining available types since 'First' is no longer available for the remaining R-1 types, and the ignored types are now P<> since we are starting a new nPr_h call).
typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type // Case 2: 'First in not in the permuted pack, so try to get R types from Rest... to append to Output... First is appended o TypesIgnored... since it is now among those types not used.
> { };
template <template <typename...> class<std::size_t PR, typename... Ts>
struct EmptyPack<P<Ts...>> { using type =Pack> P<>;struct };nPr;
template <std::size_t NR, template <typename...> class P, typename... Pack>Ts>
usingstruct PermutationNnPr<R, =P<Ts...>> typename: PermutationNHelper<NnPr_h<R, PackP<Ts...>, typenameP<>, EmptyPack<Pack>::type>::type;P<>> { };
// Testing
template <typename PackOfPacks>...> struct PackSize;P;
int main() {
static_assert(std::is_same<
nPr<2, P<int, char, float>>::type,
P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
>::value);
static_assert(std::is_same<
nPr<3, P<int, char, float, bool>>::type,
P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
>::value);
}
Incidentally, here is a longer solution, but one which has the advantage of having the implementation for nCr and all_permutations, which are useful in their own right:
#include <type_traits>
#include <utility>
namespace utilities {
template <typename... Packs> struct merge;
template <typename Pack>
struct merge<Pack> {
using type = Pack;
};
template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <typename First, typename... Rest>
struct merge<First, Rest...> : merge<First, typename merge<Rest...>::type> { };
template <typename T, typename Pack> struct prepend;
template <template <typename...> class P, typename... Ts, typename T>
struct prepend<T, P<Ts...>> {
using type = P<T, Ts...>;
};
template <typename T, typename PackOfPacks> struct prepend_to_each;
template <typename T, template <typename...> class P, typename... Packs>
struct prepend_to_each<T, P<Packs...>> {
using type = P<typename prepend<T, Packs>::type...>;
};
template <typename Pack> struct pack_size;
template <template <typename...> class P, typename... Ts>
struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
// all_rotations
template <std::size_t, typename> struct rotate;
template <template <typename...> class P, typename First, typename... Rest>
struct rotate<0, P<First, Rest...>> {
using type = P<First, Rest...>;
};
template <std::size_t N, template <typename...> class P, typename First, typename... Rest>
struct rotate<N, P<First, Rest...>> : rotate<N - 1, P<Rest..., First>> { };
template <typename Pack, typename Sequence> struct all_rotations_h;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct all_rotations_h<P<Ts...>, std::index_sequence<Is...>> {
using type = P<typename rotate<Is, P<Ts...>>::type...>;
};
template <typename Pack>
struct all_rotations : all_rotations_h<Pack, std::make_index_sequence<pack_size<Pack>::value>> { };
}
// nCr
template <std::size_t R, typename Pack, typename = void> struct nCr;
template <template <typename...> class P, typename... Ts>
struct PackSize<P<TsnCr<1, P<Ts...>> :{
std::integral_constant<std::size_t, sizeof using type = P<P<Ts>...(Ts)>>; {
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R <= sizeof...(Rest) + 1 && R > 1)>> : utilities::merge<
typename utilities::prepend_to_each<First, typename nCr<R-1, P<Rest...>>::type>::type,
typename nCr<R, P<Rest...>>::type
> { };
template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R > sizeof...(Rest) + 1)>> {
using type = P<>;
};
// all_permutations
template <typename Pack> struct all_permutations_h;
template <typename PackOfPacks> struct all_permutations_h_on_each;
template <template <typename...> class P, typename... Packs>
struct all_permutations_h_on_each<P<Packs...>> : utilities::merge<typename all_permutations_h<Packs>::type...> { };
template <typename Pack> struct
all_permutations : all_permutations_h_on_each<typename utilities::all_rotations<Pack>::type> { };
template <template <typename...> class P, typename Last>
struct all_permutations_h<P<Last>> {
using Permutationtype = PermutationN<PackSize<Pack>P<P<Last>>;
};
template <template <typename...> class P, typename First, typename... Rest>
struct all_permutations_h<P<First, Rest...>> : utilities::valueprepend_to_each<First, Pack>;typename all_permutations<P<Rest...>>::type> { };
// We now combine nCr with all_permutations to get nPr.
template <typename PackOfPacks> struct all_permutations_on_each_pack;
template <template <typename...> class P, typename... Packs>
struct all_permutations_on_each_pack<P<Packs...>> : utilities::merge<typename all_permutations<Packs>::type...> { };
template <std::size_t R, typename Pack>
struct nPr : all_permutations_on_each_pack<typename nCr<R, Pack>::type> { };
// Testing
template <typename...> struct P;
int main() {
std::cout << std::boolalpha << static_assert(std::is_same<
PermutationN<1nPr<2, P<int, char, bool>>,
P< P<int>, P<char>, P<bool> >
>::value << '\n'; // true
std::cout << stdfloat>>::is_same<
PermutationN<2, P<inttype, char, bool>>,
P< P<intP<P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
>::value << '\n'; // true
std::cout << std::is_same<
PermutationN<3, P<int, char, bool>>,
P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<charfloat>, boolP<float, int>, P<bool, int, char>P<char, P<boolfloat>, charP<float, int> >char>>
>::value << '\n'; // true);
static_assert(std::coutis_same<
<< std nPr<3, P<int, char, float, bool>>::is_same<type,
Permutation<P<intP<P<int, char, bool>>float>,
P<int, float, char>, P<char, float, int>, P<char, P<int, float>, P<float, int, char>, P<float, char, int>, P<int, char, bool>, P<int, bool, char>, P<char, bool, int>, P<char, int, bool>, P<charP<bool, int, char>, P<bool, char, int>, P<int, float, bool>, P<int, bool, float>, P<float, bool, int>, P<float, int, bool>, P<bool, int, float>, P<bool, float, int>, P<char, float, bool>, P<char, bool, float>, P<float, bool, char>, P<float, char, bool>, P<bool, char, int>float>, >P<bool, float, char>>
>::value << '\n'; // true);
}
I think I've found a better solution. It also generalizes to Permutation(N,R), with Permutation(N,N) being a special case that answers the original problem. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.
#include <iostream>
#include <type_traits>
template <typename, typename> struct Merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <std::size_t N, typename Pack, typename Previous, typename... Output> struct PermutationNHelper;
template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>, Output...> : Merge<
typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type, // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First. The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is Prev..., First.
> {};
template <std::size_t N, template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<N, P<>, Previous, Output...> {
using type = P<>;
};
template <template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<0, P<First, Rest...>, P<Prev...>, Output...> { // P<Prev...> must be used (instead of simply 'Previous'), else there will be ambiguity.
using type = P<P<Output...>>; // Note that it must be P<P<Output...>> instead of P<Output...> for the merging to work as intended.
};
template <template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<0, P<>, Previous, Output...> {
using type = P<P<Output...>>;
};
template <typename Pack> struct EmptyPack;
template <template <typename...> class P, typename... Ts>
struct EmptyPack<P<Ts...>> { using type = P<>; };
template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, Pack, typename EmptyPack<Pack>::type>::type;
template <typename PackOfPacks> struct PackSize;
template <template <typename...> class P, typename... Ts>
struct PackSize<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};
template <typename Pack>
using Permutation = PermutationN<PackSize<Pack>::value, Pack>;
// Testing
template <typename...> struct P;
int main() {
std::cout << std::boolalpha << std::is_same<
PermutationN<1, P<int, char, bool>>,
P< P<int>, P<char>, P<bool> >
>::value << '\n'; // true
std::cout << std::is_same<
PermutationN<2, P<int, char, bool>>,
P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
>::value << '\n'; // true
std::cout << std::is_same<
PermutationN<3, P<int, char, bool>>,
P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
>::value << '\n'; // true
std::cout << std::is_same<
Permutation<P<int, char, bool>>,
P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
>::value << '\n'; // true
}
I think I've found a better solution. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.
#include <type_traits>
#include <utility>
namespace utilities {
template <typename Pack1, typename Pack2> struct merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
}
template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;
template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
using type = P<P<Output...>>; // Just one single pack (which must be wrapped in P so that the resulting merge will gave all such single packs, rather than a merge of all the types).
};
template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
using type = P<>; // No pack can come out of this (permuting R types from nothing).
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
using type = P<>; // No pack can come out of this (since there are less types in P<TypesIgnored..., Rest...> than R).
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type, // Case 1: 'First' is in the permuted pack (note that Output..., First are the types in just one pack). Now continue to get R-1 more types from TypesIgnored..., Rest... (which are the remaining available types since 'First' is no longer available for the remaining R-1 types, and the ignored types are now P<> since we are starting a new nPr_h call).
typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type // Case 2: 'First in not in the permuted pack, so try to get R types from Rest... to append to Output... First is appended o TypesIgnored... since it is now among those types not used.
> { };
template <std::size_t R, typename Pack> struct nPr;
template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };
// Testing
template <typename...> struct P;
int main() {
static_assert(std::is_same<
nPr<2, P<int, char, float>>::type,
P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
>::value);
static_assert(std::is_same<
nPr<3, P<int, char, float, bool>>::type,
P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
>::value);
}
Incidentally, here is a longer solution, but one which has the advantage of having the implementation for nCr and all_permutations, which are useful in their own right:
#include <type_traits>
#include <utility>
namespace utilities {
template <typename... Packs> struct merge;
template <typename Pack>
struct merge<Pack> {
using type = Pack;
};
template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <typename First, typename... Rest>
struct merge<First, Rest...> : merge<First, typename merge<Rest...>::type> { };
template <typename T, typename Pack> struct prepend;
template <template <typename...> class P, typename... Ts, typename T>
struct prepend<T, P<Ts...>> {
using type = P<T, Ts...>;
};
template <typename T, typename PackOfPacks> struct prepend_to_each;
template <typename T, template <typename...> class P, typename... Packs>
struct prepend_to_each<T, P<Packs...>> {
using type = P<typename prepend<T, Packs>::type...>;
};
template <typename Pack> struct pack_size;
template <template <typename...> class P, typename... Ts>
struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
// all_rotations
template <std::size_t, typename> struct rotate;
template <template <typename...> class P, typename First, typename... Rest>
struct rotate<0, P<First, Rest...>> {
using type = P<First, Rest...>;
};
template <std::size_t N, template <typename...> class P, typename First, typename... Rest>
struct rotate<N, P<First, Rest...>> : rotate<N - 1, P<Rest..., First>> { };
template <typename Pack, typename Sequence> struct all_rotations_h;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct all_rotations_h<P<Ts...>, std::index_sequence<Is...>> {
using type = P<typename rotate<Is, P<Ts...>>::type...>;
};
template <typename Pack>
struct all_rotations : all_rotations_h<Pack, std::make_index_sequence<pack_size<Pack>::value>> { };
}
// nCr
template <std::size_t R, typename Pack, typename = void> struct nCr;
template <template <typename...> class P, typename... Ts>
struct nCr<1, P<Ts...>> {
using type = P<P<Ts>...>;
};
template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R <= sizeof...(Rest) + 1 && R > 1)>> : utilities::merge<
typename utilities::prepend_to_each<First, typename nCr<R-1, P<Rest...>>::type>::type,
typename nCr<R, P<Rest...>>::type
> { };
template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R > sizeof...(Rest) + 1)>> {
using type = P<>;
};
// all_permutations
template <typename Pack> struct all_permutations_h;
template <typename PackOfPacks> struct all_permutations_h_on_each;
template <template <typename...> class P, typename... Packs>
struct all_permutations_h_on_each<P<Packs...>> : utilities::merge<typename all_permutations_h<Packs>::type...> { };
template <typename Pack> struct
all_permutations : all_permutations_h_on_each<typename utilities::all_rotations<Pack>::type> { };
template <template <typename...> class P, typename Last>
struct all_permutations_h<P<Last>> {
using type = P<P<Last>>;
};
template <template <typename...> class P, typename First, typename... Rest>
struct all_permutations_h<P<First, Rest...>> : utilities::prepend_to_each<First, typename all_permutations<P<Rest...>>::type> { };
// We now combine nCr with all_permutations to get nPr.
template <typename PackOfPacks> struct all_permutations_on_each_pack;
template <template <typename...> class P, typename... Packs>
struct all_permutations_on_each_pack<P<Packs...>> : utilities::merge<typename all_permutations<Packs>::type...> { };
template <std::size_t R, typename Pack>
struct nPr : all_permutations_on_each_pack<typename nCr<R, Pack>::type> { };
// Testing
template <typename...> struct P;
int main() {
static_assert(std::is_same<
nPr<2, P<int, char, float>>::type,
P<P<int, char>, P<char, int>, P<int, float>, P<float, int>, P<char, float>, P<float, char>>
>::value);
static_assert(std::is_same<
nPr<3, P<int, char, float, bool>>::type,
P<P<int, char, float>, P<int, float, char>, P<char, float, int>, P<char, int, float>, P<float, int, char>, P<float, char, int>, P<int, char, bool>, P<int, bool, char>, P<char, bool, int>, P<char, int, bool>, P<bool, int, char>, P<bool, char, int>, P<int, float, bool>, P<int, bool, float>, P<float, bool, int>, P<float, int, bool>, P<bool, int, float>, P<bool, float, int>, P<char, float, bool>, P<char, bool, float>, P<float, bool, char>, P<float, char, bool>, P<bool, char, float>, P<bool, float, char>>
>::value);
}
I think I've found a better solution. It also generalizes to Permutation(N,R), with Permutation(N,N) being a special case that answers the original problem. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.
#include <iostream>
#include <type_traits>
template <typename, typename> struct Merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <std::size_t N, typename Pack, typename Previous, typename... Output> struct PermutationNHelper;
template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>, Output...> : Merge<
typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type, // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First. The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is Prev..., First.
> {};
template <std::size_t N, template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<N, P<>, Previous, Output...> {
using type = P<>;
};
template <template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<0, P<First, Rest...>, P<Prev...>, Output...> { // P<Prev...> must be used (instead of simply 'Previous'), else there will be ambiguity.
using type = P<P<Output...>>; // Note that it must be P<P<Output...>> instead of P<Output...> for the merging to work as intended.
};
template <template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<0, P<>, Previous, Output...> {
using type = P<P<Output...>>;
};
template <typename Pack> struct EmptyPack;
template <template <typename...> class P, typename... Ts>
struct EmptyPack<P<Ts...>> { using type = P<>; };
template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, Pack, typename EmptyPack<Pack>::type>::type;
template <typename PackOfPacks> struct PackSize;
template <template <typename...> class P, typename... Ts>
struct PackSize<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};
template <typename Pack>
using Permutation = PermutationN<PackSize<Pack>::value, Pack>;
// Testing
template <typename...> struct P;
int main() {
std::cout << std::boolalpha << std::is_same<
PermutationN<1, P<int, char, bool>>,
P< P<int>, P<char>, P<bool> >
>::value << '\n'; // true
std::cout << std::is_same<
PermutationN<2, P<int, char, bool>>,
P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
>::value << '\n'; // true
std::cout << std::is_same<
PermutationN<3, P<int, char, bool>>,
P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
>::value << '\n'; // true
std::cout << std::is_same<
Permutation<P<int, char, bool>>,
P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
>::value << '\n'; // true
}