Skip to main content
32 of 36
added 24 characters in body
user2296177
  • 3.6k
  • 1
  • 16
  • 37

Integer packs (and integer pack utilities) for template meta-programming

##Introduction

In template meta-programming, integer sequences and ranges are very useful. I've made a couple of utility classes that do various operations on compile-time integer packs.

The implementation is non-recursive (except for \$log(n)\$ recursions when generating a pack), allowing faster compilation and a greater number of template arguments.

Note: In every implementation section, at the top of every namespace, there's a comment indicating which feature is inside.


#Part 1

Part 1 consists of the basic operations: creating sequences and ranges, getting the size of a pack, negating a pack of integers, indexing into a pack, and concatenation.

###Overview

  • integer_pack<T, T...>, a pack of integers of type T.
  • index_pack<std::size_t...>, an alias of integer_pack<std::size_t, std::size_t...>.
  • integer_pack_size<T>, returns the size (number of integers in) an integer pack.
  • integer_pack_negate_t<T>, negates the integers of an integer pack.
  • make_integer_sequence<T, T n>, make an integer_pack<> type with values being in the range \$[ 0, n - 1 )\$ when \$n >= 0\$ or \$[ 0, n + 1 )\$ when \$n < 0\$.
  • make_integer_range<T, T from, T to, T step>, make an integer_pack<> with values in the range \$[ from, to ]\$ using an increment of step.
  • integer_pack_concatenate<L, R>, concatenates the right pack to the left pack.

##Usage/Tests

int main()
{
    using namespace ct;

    static_assert(integer_pack_size_v<integer_pack<int, -1, -3, 5, -6>> == 4);

    using negated_t = integer_pack_negate_t<integer_pack<short, -1, -3, 5, -6>>;
    static_assert(std::is_same_v<negated_t, integer_pack<short, 1, 3, -5, 6>>);

    using seq_to5 = make_integer_sequence<int, 5>;
    static_assert(std::is_same_v<seq_to5, integer_pack<int, 0, 1, 2, 3, 4>>);

    using seq_to_minus5 = make_integer_sequence<int, -5>;
    static_assert(std::is_same_v<seq_to_minus5, integer_pack<int, 0, -1, -2, -3, -4>>);

    using range_minus2_3 = make_integer_range<int, -2, 3>;
    static_assert(std::is_same_v<range_minus2_3, integer_pack<int, -2, -1, 0, 1, 2, 3>>);

    using range_3_minus2 = make_integer_range<int, 3, -2>;
    static_assert(std::is_same_v<range_3_minus2, integer_pack<int, 3, 2, 1, 0, -1, -2>>);

    // increasing range, step of 2
    using range_multiplesof2 = make_index_range<0, 8, 2>;
    static_assert(std::is_same_v<range_multiplesof2, index_pack<0, 2, 4, 6, 8>>);

    // decreasing range, step of 3
    using range_3_to_minus9 = make_integer_range<char, 3, -9, 3>;
    static_assert(std::is_same_v<range_3_to_minus9, integer_pack<char, 3, 0, -3, -6, -9>>);

    static_assert(integer_pack_at_v<3, integer_pack<int, -1, -3, 5, -6>> == -6);

    using c_t = integer_pack_concatenate_t<integer_pack<int, 1>, integer_pack<long, 2>>;
    static_assert(std::is_same_v<c_t, integer_pack<long, 1, 2>>);

}

##Implementation

Note: Include guard omitted.

#include <cstddef>
#include <array>
#include <limits>
#include <type_traits>

// integer_pack, index_pack
namespace ct
{
    template<class T, T... ints>
    struct integer_pack
    {
        static_assert(std::is_integral<T>::value,
            "integer_pack: first template argument must be an integer type");

        using integer_type = T;
        using type = integer_pack<T, ints...>;
    };

    template<std::size_t... ints>
    using index_pack = integer_pack<std::size_t, ints...>;
}

// integer_pack_size
namespace ct
{
    template<class IntegerPack>
    struct integer_pack_size;

    template<class T, T... ints>
    struct integer_pack_size<integer_pack<T, ints...>>
        : std::integral_constant<std::size_t, sizeof...(ints)>
    {};

    template<class IntegerPack>
    constexpr auto integer_pack_size_v = integer_pack_size<IntegerPack>::value;
}

// integer_pack_negate
namespace ct
{
    template<class IntegerPack>
    struct integer_pack_negate;

    template<class T, T... ints>
    struct integer_pack_negate<integer_pack<T, ints...>>
        : integer_pack<T, (-ints)...>
    {};

    template<class IntegerPack>
    using integer_pack_negate_t = typename integer_pack_negate<IntegerPack>::type;
}

// make_integer_sequence, make_index_sequence
namespace ct
{
    namespace impl
    {
        template<class T, class LIntegerPack, class RIntegerPack>
        struct integer_pack_merge;

        template<class T, T... l_ints, T... r_ints>
        struct integer_pack_merge
        <
            T, integer_pack<T, l_ints...>, integer_pack<T, r_ints...>
        > : integer_pack<T, l_ints..., sizeof...(l_ints) + r_ints...>
        {};

        template<class T, T n, class = void>
        struct integer_sequence_generate
            : integer_pack_merge
            <
                T,
                typename integer_sequence_generate<T, n / 2>::type,
                typename integer_sequence_generate<T, n / 2 + n % 2>::type
            >
        {};

        template<class T, T n>
        struct integer_sequence_generate<T, n, std::enable_if_t<(n == 1)>>
            : integer_pack<T, 0>
        {};

        template<class T, T n>
        struct integer_sequence_generate<T, n, std::enable_if_t<(n == 0)>>
            : integer_pack<T>
        {};

        template<class T, T n>
        struct integer_sequence_generate<T, n, std::enable_if_t<(n < 0)>>
        {
            using type = typename integer_pack_negate
            <
                typename integer_sequence_generate<T, -n>::type
            >::type;
        };
    }

    template<class T, T n>
    using make_integer_sequence = typename impl::integer_sequence_generate<T, n>::type;

    template<std::size_t n>
    using make_index_sequence = make_integer_sequence<std::size_t, n>;
}

// make_integer_range, make_index_range
namespace ct
{
    namespace impl
    {
        template
        <
            class T,
            T from,
            T to,
            T step,
            T n_vals = (from < to ? to - from : from - to)
        >
        struct integer_range_generate
        {
        private:
            static_assert(n_vals % step == 0,
                "integer_range_generate: unreachable integer range; bad step value");

            template<class IntegerPack, bool is_increasing>
            struct integer_range_generate_impl;

            template<T... ints>
            struct integer_range_generate_impl<integer_pack<T, ints...>, true>
                : integer_pack<T, (from + step * ints)...>
            {};

            template<T... ints>
            struct integer_range_generate_impl<integer_pack<T, ints...>, false>
                : integer_pack<T, (from - step * ints)...>
            {};

        public:
            using type = typename integer_range_generate_impl
            <
                make_integer_sequence<T, 1 + n_vals / step>, (from < to)
            >::type;
        };

        template<class T, T n, T step, T n_vals>
        struct integer_range_generate<T, n, n, step, n_vals>
            : integer_pack<T, n>
        {};
    }

    template<class T, T from, T to, T step = 1>
    using make_integer_range = typename impl::integer_range_generate
    <
        T, from, to, step
    >::type;

    template<std::size_t from, std::size_t to, std::size_t step = 1>
    using make_index_range = make_integer_range<std::size_t, from, to, step>;
}

// integer_pack_at
namespace ct
{
    namespace impl
    {
        template<class T, T... ints>
        constexpr auto at(std::size_t const i, integer_pack<T, ints...>) noexcept
        {
            constexpr T values[] = { ints... };
            return values[i];
        }
    }

    template<std::size_t i, class IntegerPack>
    struct integer_pack_at
        : std::integral_constant
        <
            typename IntegerPack::integer_type, impl::at(i, IntegerPack{})
        >
    {
        static_assert(integer_pack_size<IntegerPack>::value != 0,
            "integer_pack_at: empty integer pack");
        
        static_assert(i < integer_pack_size<IntegerPack>::value,
            "integer_pack_at: index out of bounds");
    };

    template<std::size_t i, class IntegerPack>
    constexpr auto integer_pack_at_v = integer_pack_at<i, IntegerPack>::value;
}

// integer_pack_concatenate
namespace ct
{
    template<class LIntegerPack, class RIntegerPack>
    struct integer_pack_concatenate;

    template<class T, class U, T... l_ints, U... r_ints>
    struct integer_pack_concatenate
    <
        integer_pack<T, l_ints...>, integer_pack<U, r_ints...>
    >
        : integer_pack<std::common_type_t<T, U>, l_ints..., r_ints...>
    {};

    template<class LIntegerPack, class RIntegerPack>
    using integer_pack_concatenate_t = typename integer_pack_concatenate
    <
        LIntegerPack, RIntegerPack
    >::type;
}

// index_sequence_shift, integer_pack_shift
namespace ct
{
    namespace impl
    {
        // right to left shift
        template
        <
            std::size_t src,
            std::size_t dst,
            class IndexSequence,
            bool is_right_to_left
        >
        struct index_sequence_shift_impl
        {
            using m_seq = make_index_range
            <
                src, integer_pack_size<IndexSequence>::value - 1
            >;
            
            using r_seq = make_index_sequence<src - dst>;
           
            using l_seq = std::conditional_t
            <
                (dst == 0),
                index_pack<>,
                make_index_range<integer_pack_size<r_seq>::value, src - 1>
            >;
        };

        // left to right shift
        template<std::size_t src, std::size_t dst, class IndexSequence>
        struct index_sequence_shift_impl<src, dst, IndexSequence, false>
        {
            static constexpr auto last_offset
            {
                integer_pack_size<IndexSequence>::value - 1
            };

            using m_seq = make_index_sequence<src>;
            using r_seq = make_index_range<src, src + (last_offset - dst)>;
            using l_seq = make_index_range
            <
                integer_pack_at<integer_pack_size<r_seq>::value - 1, r_seq>::value + 1,
                last_offset
            >;
        };
    }

    template<std::size_t src, std::size_t dst, class IndexSequence>
    struct index_sequence_shift
    {
    private:
        static_assert(src < integer_pack_size<IndexSequence>::value,
            "index_sequence_shift: src index out of range");

        static_assert(dst < integer_pack_size<IndexSequence>::value,
            "index_sequence_shift: dst index out of range");

        static_assert(integer_pack_size<IndexSequence>::value != 0,
            "index_sequence_shift: empty index sequence");

        using shift_t = impl::index_sequence_shift_impl
        <
            src, dst, IndexSequence, (src > dst)
        >;

        using l_seq = typename shift_t::l_seq;
        using m_seq = typename shift_t::m_seq;
        using r_seq = typename shift_t::r_seq;

    public:
        using type = integer_pack_concatenate_t
        <
            l_seq, integer_pack_concatenate_t<m_seq, r_seq>
        >;
    };

    template<std::size_t dst, class IndexSequence>
    struct index_sequence_shift<dst, dst, IndexSequence>
    {
        static_assert(integer_pack_size<IndexSequence>::value != 0,
            "index_sequence_shift: empty index sequence");

        using type = IndexSequence;
    };

    template<std::size_t src, std::size_t dst, class IndexSequence>
    using index_sequence_shift_t = typename index_sequence_shift
    <
        src, dst, IndexSequence
    >::type;

    template<std::size_t src, std::size_t dst, class IntegerPack>
    struct integer_pack_shift
    {
    private:
        template<class IndexSequence>
        struct integer_pack_shift_impl;

        template<std::size_t... is>
        struct integer_pack_shift_impl<index_pack<is...>>
        {
            using type = integer_pack
            <
                typename IntegerPack::integer_type,
                integer_pack_at<is, IntegerPack>::value...
            >;
        };

    public:
        using type = typename integer_pack_shift_impl
        <
            index_sequence_shift_t
            <
                src,
                dst,
                make_index_sequence<integer_pack_size<IntegerPack>::value>
            >
        >::type;
    };

    template<std::size_t src, std::size_t dst, class IntegerPack>
    using integer_pack_shift_t = typename integer_pack_shift
    <
        src, dst, IntegerPack
    >::type;
}

// integer_pack_minimum, integer_pack_maximum
namespace ct
{
    namespace impl
    {
        struct smallest
        {
            template<class T>
            constexpr auto operator()(T const& lhs, T const& rhs) noexcept
            {
                return lhs > rhs;
            }
        };
        
        struct largest
        {
            template<class T>
            constexpr auto operator()(T const& lhs, T const& rhs) noexcept
            {
                return lhs < rhs;
            }
        };

        template<class Predicate, class T, T... ints>
        constexpr auto find_integer(Predicate p, integer_pack<T, ints...>) noexcept
        {
            T integers[] = { ints... };
            T value{ 0 };

            for (std::size_t i{ 0 }; i < sizeof...(ints); ++i)
                if (p(value, integers[i]))
                    value = integers[i];

            return value;
        }
    }

    template<class IntegerPack>
    struct integer_pack_minimum
        : std::integral_constant
        <
            typename IntegerPack::integer_type,
            impl::find_integer(impl::smallest{}, IntegerPack{})
        >
    {
        static_assert(integer_pack_size<IntegerPack>::value != 0,
            "integer_pack_minimum: empty integer pack");
    };

    template<class IntegerPack>
    constexpr auto integer_pack_minimum_v = integer_pack_minimum<IntegerPack>::value;

    template<class IntegerPack>
    struct integer_pack_maximum
        : std::integral_constant
        <
            typename IntegerPack::integer_type,
            impl::find_integer(impl::largest{}, IntegerPack{})
        >
    {
        static_assert(integer_pack_size<IntegerPack>::value != 0,
            "integer_pack_maximum: empty integer pack");
    };

    template<class IntegerPack>
    constexpr auto integer_pack_maximum_v = integer_pack_maximum<IntegerPack>::value;
}

#Part 2

Part 2 consists of manipulating an integer pack: shifting the pack left or right (where values wrap around), sorting a pack, and set operations on integer packs.

###Overview

  • integer_pack_offset<offset, T, bool>, will apply an offset to the template sequence in increasing or decreasing fashion.
  • integer_pack_shift<src, dst, T>, shifts the elements starting from the element at the src index to the position of the element at the dst index; integers wrap around as needed.
  • integer_pack_sort<T>, sorts the template argument integer pack by ascending order.
  • integer_pack_union<L, R>, performs the set union of two integer packs; the output set is sorted.
  • integer_pack_intersection<L, R>, performs the set intersection of two integer packs; the output set is sorted.
  • integer_pack_complement<L, R>, performs the set intersection of two integer packs; the output set is sorted and its integer type will be the same integer type as the integer pack specified as the left template argument.

###Usage/tests

int main()
{
    using namespace ct;

    // increasing offset
    using ioffset_t = integer_pack<int, -2, -1, 0>;
    using expected_ioffset_t = integer_pack<int, 0, 1, 2>;
    using result_ioffset_t = integer_pack_offset_t<2, ioffset_t, true>;
    static_assert(std::is_same_v<result_ioffset_t, expected_ioffset_t>);

    // decreasing offset
    using doffset_t = integer_pack<int, -2, -1, 0>;
    using expected_doffset_t = integer_pack<int, -4, -3, -2>;
    using result_doffset_t = integer_pack_offset<2, doffset_t, false>::type;
    static_assert(std::is_same_v<result_doffset_t, expected_doffset_t>);

    // left to right shift
    using pack_t = integer_pack<int, 1, 3, 5, 7, 11>;
    using l_expected_t = integer_pack<int, 7, 11, 1, 3, 5>;
    static_assert(std::is_same_v<integer_pack_shift_t<1, 3, pack_t>, l_expected_t>);

    // right to left shift
    using r_expected_t = integer_pack<int, 5, 7, 11, 1, 3>;
    static_assert(std::is_same_v<integer_pack_shift_t<2, 0, pack_t>, r_expected_t>);

    // more shifting
    using shift_t = integer_pack<long, -3, -5, 1, -7, 11>;
    using expected_shift_t = integer_pack<long, -5, 1, -7, 11, -3>;
    static_assert(std::is_same_v<integer_pack_shift_t<0, 4, shift_t>, expected_shift_t>);

    // sorting
    using l_sorted_t = integer_pack_sort_t<l_expected_t>;
    using r_sorted_t = integer_pack_sort_t<r_expected_t>;
    static_assert(std::is_same_v<l_sorted_t, r_sorted_t>);

    // more sorting
    using to_sort_t = integer_pack<long, 5, 3, 17, -15, 8, -3, -23, 1>;
    using expected_sorted_t = integer_pack<long, -23, -15, -3, 1, 3, 5, 8, 17>;
    static_assert(std::is_same_v<integer_pack_sort_t<to_sort_t>, expected_sorted_t>);

    // union
    using union_left_t = integer_pack<int, 1, -2, 3, -4>;
    using union_right_t = integer_pack<int, -1, -2, -3, -4>;
    using expected_union_t = integer_pack<int, -4, -3, -2, -1, 1, 3>;

    using union_t = integer_pack_union<union_left_t, union_right_t>::type;
    static_assert(std::is_same_v<union_t, expected_union_t>);

    // intersection
    using l_intersec_t = integer_pack<int, -5, 2, 8, 6, 3>;
    using r_intersec_t = integer_pack<int, 6, 4, 2, 2, 3, 9>;
    using expected_intersec_t = integer_pack<int, 2, 3, 6>;

    using intersection_t = integer_pack_instersection<l_intersec_t, r_intersec_t>::type;
    static_assert(std::is_same_v<intersection_t, expected_intersec_t>);

    // complement
    using l_comp_t0 = integer_pack<char, -1, 0, 1>;
    using r_comp_t0 = integer_pack<int, 1, 2, 3, -1, 4>;
    using expected_comp_t0 = integer_pack<char, 0>;
    using result_comp_t0 = integer_pack_complement<l_comp_t0, r_comp_t0>::type;
    static_assert(std::is_same_v<result_comp_t0, expected_comp_t0>);

    using l_comp_t1 = integer_pack<int, -1, 1, 2, 3, 4>;
    using r_comp_t1 = integer_pack<char, 0, 1>;
    using expected_comp_t1 = integer_pack<int, -1, 2, 3, 4>;
    using result_comp_t1 = integer_pack_complement<l_comp_t1, r_comp_t1>::type;
    static_assert(std::is_same_v<result_comp_t1, expected_comp_t1>);
}

###Implementation

Note: Include guard omitted.

// integer_pack_offset
namespace ct
{
    template<std::size_t offset, class IntegerPack, bool increasing, class = void>
    struct integer_pack_offset;

    template<class IntegerPack, bool increasing>
    struct integer_pack_offset<0, IntegerPack, increasing>
        : IntegerPack
    {};

    template<std::size_t offset, class T, T... ints>
    struct integer_pack_offset
    <
        offset,
        integer_pack<T, ints...>,
        true,
        std::enable_if_t<(offset != 0)>
    >
        : integer_pack<T, (ints + static_cast<T>(offset))...>
    {
        static_assert(offset <= std::numeric_limits<T>::max(),
            "integer_pack_offset: offset overflow");
    };

    template<std::size_t offset, class T, T... ints>
    struct integer_pack_offset
    <
        offset,
        integer_pack<T, ints...>,
        false,
        std::enable_if_t<(offset != 0)>
    >
        : integer_pack<T, (ints - static_cast<T>(offset))...>
    {
        static_assert(offset <= std::numeric_limits<T>::max(),
            "integer_pack_offset: offset overflow");
    };

    template<std::size_t offset, class IntegerPack, bool increasing>
    using integer_pack_offset_t = typename integer_pack_offset
    <
        offset, IntegerPack, increasing
    >::type;
}

// integer_pack_apply
namespace ct
{
    namespace impl
    {
        template<class T, std::size_t n>
        constexpr auto array_value_type(T const(&)[n]) noexcept -> T;

        template<class T, std::size_t n>
        constexpr auto array_value_type(std::array<T, n> const&) noexcept -> T;

        template<class T>
        using array_value_type_t = decltype(array_value_type(T{}));

        template<class T, std::size_t n>
        constexpr auto array_size(T const(&)[n]) noexcept
        {
            return n;
        }

        template<class T, std::size_t n>
        constexpr auto array_size(std::array<T, n> const&) noexcept
        {
            return n;
        }

        template<class Function, class IntegerPack, class... IntegerPacks>
        struct integer_pack_apply
        {
        private:
            using array_t = decltype(Function{}(IntegerPack{}, IntegerPacks{}...));

            template<std::size_t... is>
            static constexpr auto apply(index_pack<is...>) noexcept
            {
                constexpr auto integers{ Function{}(IntegerPack{}, IntegerPacks{}...) };
                return integer_pack<array_value_type_t<array_t>, integers[is]...>{};
            }

        public:
            using type = decltype(apply(make_index_sequence<array_size(array_t{})>{}));
        };

        template<class Function, class IntegerPack, class... IntegerPacks>
        using integer_pack_apply_t = typename integer_pack_apply
        <
            Function, IntegerPack, IntegerPacks...
        >::type;
    }
}

// integer_pack_sort
namespace ct
{
    namespace impl
    {
        template<class T, std::size_t n, std::size_t... is>
        constexpr auto to_std_array(T const(&arr)[n], index_pack<is...>) noexcept
        {
            return std::array<T, n>{ arr[is]... };
        }

        template<class T, std::size_t n>
        constexpr auto to_std_array(T const(&arr)[n]) noexcept
        {
            return to_std_array(arr, make_index_sequence<n>{});
        }

        template<class T, T... ints>
        constexpr auto to_std_array(integer_pack<T, ints...>) noexcept
        {
            return std::array<T, sizeof...(ints)>{ ints... };
        }

        template<class T, std::size_t n>
        struct make_array
        {
            template<class U>
            static constexpr auto from_sort(T const offset, U const& counts) noexcept
            {
                T sorted_integers[n] = {};
                
                for (std::size_t i{ 0 }, k{ 0 }; i < array_size(counts); i++)
                    for (std::size_t j{ 0 }; j < counts[i]; j++)
                        sorted_integers[k++] = static_cast<T>(i) - offset;

                return to_std_array(sorted_integers);
            }

            template<class U>
            static constexpr auto from_union(T const offset, U const& map) noexcept
            {
                T union_integers[n] = {};
                
                for (std::size_t i{ 0 }, k{ 0 }; i < map.size(); ++i)
                    if (map[i])
                        union_integers[k++] = static_cast<T>(i) - offset;

                return to_std_array(union_integers);
            }

            template<class U, class V>
            static constexpr auto from_intersection(
                T const offset, U const& l_map, V const& r_map) noexcept
            {
                T intersection_integers[n] = {};

                for (std::size_t i{ 0 }, k{ 0 }; i < r_map.size(); ++i)
                    if (l_map[r_map[i]])
                        intersection_integers[k++] = r_map[i] - offset;

                return to_std_array(intersection_integers);
            }
        };

        template<class T>
        struct make_array<T, 0>
        {
            template<class U>
            static constexpr auto from_sort(T const, U const&) noexcept
            {
                return std::array<T, 0>{};
            }

            template<class U>
            static constexpr auto from_union(T const, U const&) noexcept
            {
                return std::array<T, 0>{};
            }

            template<class U, class V>
            static constexpr auto from_intersection(T const, U const&, V const&) noexcept
            {
                return std::array<T, 0>{};
            }
        };

        template<class T, T... ints>
        constexpr auto counting_sort(integer_pack<T, ints...>) noexcept
        {
            using pack_t = integer_pack<T, ints...>;

            constexpr auto min{ integer_pack_minimum<pack_t>::value };
            constexpr auto has_negative{ min < 0 };

            using offset_pack_t = std::conditional_t
            <
                has_negative,
                integer_pack_offset_t<-min, pack_t, true>,
                pack_t
            >;

            T integers[] = { (has_negative ? ints - min : ints)... };

            std::size_t counts[integer_pack_maximum<offset_pack_t>::value + 1] = {};

            for (std::size_t i{ 0 }; i < sizeof...(ints); ++i)
                ++counts[integers[i]];

            return make_array
            <
                T, sizeof...(ints)
            >::from_sort(has_negative ? -min : 0, counts);
        }

        template<class T>
        constexpr auto counting_sort(integer_pack<T>) noexcept
        {
            return std::array<T, 0>{};
        }
    }

    template<class IntegerPack, class = void>
    struct integer_pack_sort
    {
    private:
        struct sort_t
        {
            constexpr auto operator()(IntegerPack) const
            {
                return impl::counting_sort(IntegerPack{});
            }
        };

    public:
        using type = impl::integer_pack_apply_t<sort_t, IntegerPack>;
    };

    template<class IntegerPack>
    using integer_pack_sort_t = typename integer_pack_sort<IntegerPack>::type;
}

// integer_pack_union
namespace ct
{
    namespace impl
    {
        template<class T, T... ints>
        constexpr auto make_found_integer_map(integer_pack<T, ints...>) noexcept
        {
            using pack_t = integer_pack<T, ints...>;

            constexpr auto min{ integer_pack_minimum<pack_t>::value };
            constexpr auto max{ integer_pack_maximum<pack_t>::value };

            constexpr T integers[] = { (min < 0 ? ints - min : ints)... };
            
            bool found[(min < 0 ? max - min : max) + 1] = {};
            
            for (std::size_t i{ 0 }; i < sizeof...(ints); ++i)
                found[integers[i]] = true;

            return to_std_array(found);
        }

        template<class T>
        constexpr auto found_integer_map_size(T const& map) noexcept
        {
            std::size_t size{ 0 };

            for (typename T::size_type i{ 0 }; i < map.size(); ++i)
                if (map[i])
                    ++size;

            return size;
        }

        template<class T, class U, T... l_ints, U... r_ints>
        constexpr auto integer_pack_union(
            integer_pack<T, l_ints...>, integer_pack<U, r_ints...>) noexcept
        {
            using integer_type = std::common_type_t<T, U>;

            constexpr auto min
            {
                integer_pack_minimum
                <
                    integer_pack<integer_type, l_ints..., r_ints...>
                >::value
            };
            
            constexpr auto map
            {
                make_found_integer_map(
                    integer_pack<integer_type, l_ints..., r_ints...>{})
            };
            
            return make_array
            <
                integer_type, found_integer_map_size(map)
            >::from_union(min < 0 ? -min : 0, map);
        }
    }

    template<class LIntegerPack, class RIntegerPack>
    struct integer_pack_union
    {
    private:
        struct union_t
        {
            constexpr auto operator()(LIntegerPack, RIntegerPack) const
            {
                return impl::integer_pack_union(LIntegerPack{}, RIntegerPack{});
            };
        };

    public:
        using type = impl::integer_pack_apply_t<union_t, LIntegerPack, RIntegerPack>;
    };

    template<class LIntegerPack, class RIntegerPack>
    using integer_pack_union_t = typename integer_pack_union
    <
        LIntegerPack, RIntegerPack
    >::type;
}

// integer_pack_instersection
namespace ct
{
    namespace impl
    {
        template<class T, class U>
        constexpr auto intersection_size(T const& lhs, U const& rhs) noexcept
        {
            std::size_t size{ 0 };

            for (std::size_t i{ 0 }; i < rhs.size(); ++i)
                if (lhs[rhs[i]])
                    ++size;

            return size;
        }

        template<class LIntegerPack, class RIntegerPack>
        constexpr auto integer_pack_equalizer_offset() noexcept
        {
            constexpr auto l{ integer_pack_minimum<LIntegerPack>::value };
            constexpr auto r{ integer_pack_minimum<RIntegerPack>::value };
    
            /*
            determining the proper offset:
                if both minimums are negative, pick smallest
                if one minimum is negative, and the other positive, pick negative
                if neither minimum is negative, offset is 0
                whenever a negative minimum is picked, make it positive (*-1)

            algorithm:
                if (l < 0 && r < 0)
                {
                    offset = l < r ? -l : -r;
                }
                else if (l < 0)
                {
                    offset = -l;
                }
                else if (r < 0)
                {
                    offset = -r;
                }
                else
                {
                    offset = 0;
                }
            */
            return l < 0 ? (r < 0 ? (l < r ? -l : -r) : -l) : (r < 0 ? -r : 0);
        }

        template<class LIntegerPack, class RIntegerPack>
        constexpr auto determine_larger_pack() noexcept
        {
            constexpr auto l_min{ integer_pack_minimum<LIntegerPack>::value };
            constexpr auto l_max{ integer_pack_maximum<LIntegerPack>::value };

            constexpr auto r_min{ integer_pack_minimum<RIntegerPack>::value };
            constexpr auto r_max{ integer_pack_maximum<RIntegerPack>::value };

            constexpr auto l_true_max{ l_min < 0 ? l_max - l_min : l_max };
            constexpr auto r_true_max{ r_min < 0 ? r_max - r_min : r_max };

            return std::conditional_t
            <
                (l_true_max > r_true_max), LIntegerPack, RIntegerPack
            >{};
        }

        template<class IntegerPack>
        struct integer_pack_remove_repetitions_and_sort
        {
        private:
            template<class T, T... ints>
            static constexpr auto remove_repetitions(integer_pack<T, ints...>) noexcept
            {
                using namespace ct;
                using namespace ct::impl;

                using pack_t = integer_pack<T, ints...>;

                constexpr auto map{ make_found_integer_map(pack_t{}) };
                constexpr auto min{ integer_pack_minimum<pack_t>::value };
                constexpr auto max{ integer_pack_maximum<pack_t>::value };
                constexpr auto offset{ min < 0 ? -min : 0 };

                T integers[found_integer_map_size(map)] = {};

                for (std::size_t i{ 0 }, k{ 0 },
                    sz{ (offset == 0 ? max : max + offset) + 1 }; i < sz; ++i)
                {
                    if (map[i])
                    {
                        integers[k++] = static_cast<T>(i) - offset;
                    }
                }
                return to_std_array(integers);
            }

            static constexpr auto integers{ remove_repetitions(IntegerPack{}) };
            static constexpr auto size{ integers.size() };

            template<std::size_t... is>
            static constexpr auto to_integer_pack(index_pack<is...>) noexcept
            {
                return integer_pack
                <
                    typename IntegerPack::integer_type, integers[is]...
                >{};
            }

        public:
            using type = decltype(to_integer_pack(make_index_sequence<size>{}));
        };

        template<class IntegerPack>
        using integer_pack_remove_repetitions_and_sort_t =
            typename integer_pack_remove_repetitions_and_sort<IntegerPack>::type;

        template<class LIntegerPack, class RIntegerPack>
        constexpr auto make_intersection() noexcept
        {
            using integer_type = std::common_type_t
            <
                typename LIntegerPack::integer_type, typename RIntegerPack::integer_type
            >;
    
            constexpr auto l_min{ integer_pack_minimum<LIntegerPack>::value };
            constexpr auto r_min{ integer_pack_minimum<RIntegerPack>::value };
            constexpr auto offset
            {
                integer_pack_equalizer_offset<LIntegerPack, RIntegerPack>()
            };
            
            using l_offset_t = std::conditional_t
            <
                (l_min < r_min),
                integer_pack_offset_t<offset, LIntegerPack, true>,
                integer_pack_offset_t<offset, LIntegerPack, true>
            >;

            using r_offset_t = std::conditional_t
            <
                (l_min < r_min),
                integer_pack_offset_t<offset, RIntegerPack, true>,
                integer_pack_offset_t<offset, RIntegerPack, true>
            >;
            
            using l_pack_t = decltype(determine_larger_pack<l_offset_t, r_offset_t>());
            using r_pack_t = std::conditional_t
            <
                std::is_same<l_pack_t, l_offset_t>::value, r_offset_t, l_offset_t
            >;

            constexpr auto l_map{ make_found_integer_map(l_pack_t{}) };
            constexpr auto r_map
            {
                to_std_array(integer_pack_remove_repetitions_and_sort_t<r_pack_t>{})
            };

            return make_array
            <
                integer_type, intersection_size(l_map, r_map)
            >::from_intersection(offset, l_map, r_map);
        }
    }

    template<class LIntegerPack, class RIntegerPack>
    struct integer_pack_instersection
    {
    private:
        struct intersection_t
        {
            constexpr auto operator()(LIntegerPack, RIntegerPack) const
            {
                return impl::make_intersection<LIntegerPack, RIntegerPack>();
            };
        };

    public:
        using type = impl::integer_pack_apply_t
        <
            intersection_t, LIntegerPack, RIntegerPack
        >;
    };

    template<class T, class RIntegerPack>
    struct integer_pack_instersection<integer_pack<T>, RIntegerPack>
        : integer_pack<std::common_type_t<T, typename RIntegerPack::integer_type>>
    {};

    template<class LIntegerPack, class T>
    struct integer_pack_instersection<LIntegerPack, integer_pack<T>>
        : integer_pack_instersection<integer_pack<T>, LIntegerPack>
    {};

    template<class T, class U>
    struct integer_pack_instersection<integer_pack<T>, integer_pack<U>>
        : integer_pack<std::common_type_t<T, U>>
    {};

    template<class LIntegerPack, class RIntegerPack>
    using integer_pack_instersection_t = typename integer_pack_instersection
    <
        LIntegerPack, RIntegerPack
    >::type;
}

// integer_pack_complement
namespace ct
{
    namespace impl
    {
        template<class T, class U, class V>
        constexpr auto complement_size(
            T const max, U const& l_map, V const& r_map) noexcept
        {
            std::size_t size{ 0 };

            /*
            l_map: <0, 1, 2, 3, 4> -> only converted
    
            r_map: <1, 2, 3> -> r_map is the one that needs to be mapped out

            complement is l_map[i] (iterator on l_map)
                -> l_map[i] < max(r_map) && !r_map[l_map[i]]
            */

            for (std::size_t i{ 0 }; i < l_map.size(); i++)
                if (l_map[i] > max || !r_map[l_map[i]])
                    ++size;

            return size;
        }

        template<class LIntegerPack, class RIntegerPack>
        constexpr auto make_complement() noexcept
        {
            constexpr auto offset
            {
                integer_pack_equalizer_offset<LIntegerPack, RIntegerPack>()
            };

            using l_pack_t = integer_pack_offset_t<offset, LIntegerPack, true>;
            using r_pack_t = integer_pack_offset_t<offset, RIntegerPack, true>;

            constexpr auto l_map{ to_std_array(l_pack_t{}) };
            constexpr auto r_map{ make_found_integer_map(r_pack_t{}) };

            constexpr auto r_map_max{ integer_pack_maximum<r_pack_t>::value };

            return make_array
            <
                typename LIntegerPack::integer_type,
                complement_size(r_map_max, l_map, r_map)
            >::from_complement(offset, r_map_max, l_map, r_map);
        }
    }

    template<class LIntegerPack, class RIntegerPack>
    struct integer_pack_complement
    {
    private:
        struct complement_t
        {
            constexpr auto operator()(LIntegerPack, RIntegerPack) const
            {
                return impl::make_complement<LIntegerPack, RIntegerPack>();
            };
        };

    public:
        using type = impl::integer_pack_apply_t
        <
            complement_t, LIntegerPack, RIntegerPack
        >;
    };

    template<class T, class RIntegerPack>
    struct integer_pack_complement<integer_pack<T>, RIntegerPack>
        : integer_pack<T>
    {};

    template<class LIntegerPack, class T>
    struct integer_pack_complement<LIntegerPack, integer_pack<T>>
        : LIntegerPack
    {};

    template<class T, class U>
    struct integer_pack_complement<integer_pack<T>, integer_pack<U>>
        : integer_pack<T>
    {};
}
user2296177
  • 3.6k
  • 1
  • 16
  • 37