#include <iostream>

template <unsigned N>
class element {
public:
    const static unsigned value = N;
};

template <typename ...Elems>
class set{};

template <typename ...Elems>
void print(set<Elems...> t) {
    std::cout << "{";
    t.print();
    std::cout << "}";
}

template <typename Elem, typename ...Elems>
class set<Elem, Elems...> {
public:
    static void print()
    {
        ::print(Elem());
        std::cout << ", ";
        set<Elems...>::print();
    }
};

template <typename Elem>
class set<Elem> {
public:
    static void print()
    {
        ::print(Elem());
    }
};

template <unsigned N, typename ...Elems>
class set<element<N>, Elems...> {
public:
    static void print()
    {
        std::cout << N << ", ";
        set<Elems...>::print();
    }
};

template <unsigned N>
class set<element<N>> {
public:
    static void print()
    {
        std::cout << N;
    }
};

template <>
class set<> {
public:
    static void print(){}
};

template <typename T, typename U>
class join;

template <typename ...Elems>
class join<set<>, set<Elems...>> {
public:
    typedef set<Elems...> type;
};

template <typename Elem1, typename ...Elems, typename ...Elems2>
class join<set<Elem1, Elems...>, set<Elems2...>> {
public:
    typedef typename join<set<Elems...>, set<Elem1, Elems2...>>::type type;
};


template <typename T, typename U>
class all_subsets_worker;

template <typename Rest>
class all_subsets_worker<set<>, Rest>
{
public:
    typedef set<Rest> type;
};

template <typename Elem, typename Rest, typename ...Elems>
class all_subsets_worker<set<Elem, Elems...>, Rest>
{
public:
    typedef typename join<
                typename all_subsets_worker<set<Elems...>, Rest>::type,
                typename all_subsets_worker<set<Elems...>, typename join< set<Elem>, Rest>::type >::type
            >::type type;
};

template <typename T>
class all_subsets;

template <typename Elem, typename ...Elems>
class all_subsets<set<Elem, Elems...>> {
public:
    typedef typename all_subsets_worker<set<Elem, Elems...>, set<>>::type type;
};



int main(int argc, const char * argv[])
{
    auto t = all_subsets<set<element<0>, element<1>, element<2>, element<3>>>::type();

    print(t);

    std::cout << "\n";
    return 0;
}