##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 typeT.index_pack<std::size_t...>, an alias ofinteger_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 aninteger_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 aninteger_pack<>with values in the range \$[ from, to ]\$ using an increment ofstep.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 thesrcindex to the position of the element at thedstindex; 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.
###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 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>
{};
}