Implement ranges::next_permutation and ranges::prev_permutation (#1099)

This commit is contained in:
Casey Carter 2020-08-05 00:02:21 -07:00 коммит произвёл GitHub
Родитель 622b62a777
Коммит 0eb754dedd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 415 добавлений и 0 удалений

Просмотреть файл

@ -9471,6 +9471,79 @@ _CONSTEXPR20 bool next_permutation(_BidIt _First, _BidIt _Last) {
return _STD next_permutation(_First, _Last, less<>{});
}
#ifdef __cpp_lib_concepts
namespace ranges {
// ALIAS TEMPLATE next_permutation_result
template <class _In>
using next_permutation_result = in_found_result<_In>;
// VARIABLE ranges::next_permutation
class _Next_permutation_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
// clang-format off
template <bidirectional_iterator _It, sentinel_for<_It> _Se, class _Pr = ranges::less, class _Pj = identity>
requires sortable<_It, _Pr, _Pj>
constexpr next_permutation_result<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
_Seek_wrapped(_First, _ULast);
const bool _Found =
_Next_permutation_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj));
return {_STD move(_First), _Found};
}
// clang-format off
template <bidirectional_range _Rng, class _Pr = ranges::less, class _Pj = identity>
requires sortable<iterator_t<_Rng>, _Pr, _Pj>
constexpr next_permutation_result<borrowed_iterator_t<_Rng>> operator()(
_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
auto _ULast = _Get_final_iterator_unwrapped(_Range);
const bool _Found = _Next_permutation_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj));
return {_Rewrap_iterator(_Range, _STD move(_ULast)), _Found};
}
private:
template <class _It, class _Pr, class _Pj>
_NODISCARD static constexpr bool _Next_permutation_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) {
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>);
auto _Next = _Last;
if (_First == _Last || _First == --_Next) {
return false;
}
for (;;) { // find rightmost element smaller than successor
auto _Next1 = _Next;
if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Next), _STD invoke(_Proj, *_Next1))) {
// swap with rightmost element that's smaller, flip suffix
auto _Mid = _Last;
do {
--_Mid;
} while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_Mid)));
_RANGES iter_swap(_Next, _Mid);
_Reverse_common(_STD move(_Next1), _STD move(_Last));
return true;
}
if (_Next == _First) { // pure descending, flip all
_Reverse_common(_STD move(_First), _STD move(_Last));
return false;
}
}
}
};
inline constexpr _Next_permutation_fn next_permutation{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
// FUNCTION TEMPLATE prev_permutation
template <class _BidIt, class _Pr>
_CONSTEXPR20 bool prev_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) {
@ -9509,6 +9582,79 @@ _CONSTEXPR20 bool prev_permutation(_BidIt _First, _BidIt _Last) {
return _STD prev_permutation(_First, _Last, less<>{});
}
#ifdef __cpp_lib_concepts
namespace ranges {
// ALIAS TEMPLATE prev_permutation_result
template <class _In>
using prev_permutation_result = in_found_result<_In>;
// VARIABLE ranges::prev_permutation
class _Prev_permutation_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
// clang-format off
template <bidirectional_iterator _It, sentinel_for<_It> _Se, class _Pr = ranges::less, class _Pj = identity>
requires sortable<_It, _Pr, _Pj>
constexpr prev_permutation_result<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
_Seek_wrapped(_First, _ULast);
const bool _Found =
_Prev_permutation_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj));
return {_STD move(_First), _Found};
}
// clang-format off
template <bidirectional_range _Rng, class _Pr = ranges::less, class _Pj = identity>
requires sortable<iterator_t<_Rng>, _Pr, _Pj>
constexpr prev_permutation_result<borrowed_iterator_t<_Rng>> operator()(
_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const {
// clang-format on
auto _ULast = _Get_final_iterator_unwrapped(_Range);
const bool _Found = _Prev_permutation_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj));
return {_Rewrap_iterator(_Range, _STD move(_ULast)), _Found};
}
private:
template <class _It, class _Pr, class _Pj>
_NODISCARD static constexpr bool _Prev_permutation_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) {
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>);
auto _Next = _Last;
if (_First == _Last || _First == --_Next) {
return false;
}
for (;;) { // find rightmost element not smaller than successor
auto _Next1 = _Next;
if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next1), _STD invoke(_Proj, *--_Next))) {
// swap with rightmost element that's not smaller, flip suffix
auto _Mid = _Last;
do {
--_Mid;
} while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_Next)));
_RANGES iter_swap(_Next, _Mid);
_Reverse_common(_STD move(_Next1), _STD move(_Last));
return true;
}
if (_Next == _First) { // pure ascending, flip all
_Reverse_common(_STD move(_First), _STD move(_Last));
return false;
}
}
}
};
inline constexpr _Prev_permutation_fn prev_permutation{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
// FUNCTION TEMPLATES is_sorted AND is_sorted_until
template <class _FwdIt, class _Pr>
_NODISCARD _CONSTEXPR20 _FwdIt is_sorted_until(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) {

Просмотреть файл

@ -274,6 +274,7 @@ tests\P0896R4_ranges_alg_nth_element
tests\P0896R4_ranges_alg_partition
tests\P0896R4_ranges_alg_partition_copy
tests\P0896R4_ranges_alg_partition_point
tests\P0896R4_ranges_alg_permutations
tests\P0896R4_ranges_alg_remove
tests\P0896R4_ranges_alg_remove_copy
tests\P0896R4_ranges_alg_remove_copy_if

Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\concepts_matrix.lst

Просмотреть файл

@ -0,0 +1,264 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <algorithm>
#include <cassert>
#include <concepts>
#include <cstdio>
#include <random>
#include <ranges>
#include <utility>
#include <range_algorithm_support.hpp>
using namespace std;
#pragma warning(disable : 6294) // Ill-defined for-loop: initial condition does not satisfy test. Loop body not executed
// Validate that (next|prev)_permutation_result alias in_found_result
STATIC_ASSERT(same_as<ranges::next_permutation_result<int>, ranges::in_found_result<int>>);
STATIC_ASSERT(same_as<ranges::prev_permutation_result<int>, ranges::in_found_result<int>>);
// Validate dangling story
STATIC_ASSERT(
same_as<decltype(ranges::next_permutation(borrowed<false>{})), ranges::next_permutation_result<ranges::dangling>>);
STATIC_ASSERT(same_as<decltype(ranges::next_permutation(borrowed<true>{})), ranges::next_permutation_result<int*>>);
STATIC_ASSERT(
same_as<decltype(ranges::prev_permutation(borrowed<false>{})), ranges::prev_permutation_result<ranges::dangling>>);
STATIC_ASSERT(same_as<decltype(ranges::prev_permutation(borrowed<true>{})), ranges::prev_permutation_result<int*>>);
constexpr int perm1[][1] = {{0}};
constexpr int perm4[][4] = {{0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1},
{1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 0, 3, 1},
{2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0}, {3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0},
{3, 2, 0, 1}, {3, 2, 1, 0}};
constexpr int perm5[][5] = {{0, 1, 2, 3, 4}, {0, 1, 2, 4, 3}, {0, 1, 3, 2, 4}, {0, 1, 3, 4, 2}, {0, 1, 4, 2, 3},
{0, 1, 4, 3, 2}, {0, 2, 1, 3, 4}, {0, 2, 1, 4, 3}, {0, 2, 3, 1, 4}, {0, 2, 3, 4, 1}, {0, 2, 4, 1, 3},
{0, 2, 4, 3, 1}, {0, 3, 1, 2, 4}, {0, 3, 1, 4, 2}, {0, 3, 2, 1, 4}, {0, 3, 2, 4, 1}, {0, 3, 4, 1, 2},
{0, 3, 4, 2, 1}, {0, 4, 1, 2, 3}, {0, 4, 1, 3, 2}, {0, 4, 2, 1, 3}, {0, 4, 2, 3, 1}, {0, 4, 3, 1, 2},
{0, 4, 3, 2, 1}, {1, 0, 2, 3, 4}, {1, 0, 2, 4, 3}, {1, 0, 3, 2, 4}, {1, 0, 3, 4, 2}, {1, 0, 4, 2, 3},
{1, 0, 4, 3, 2}, {1, 2, 0, 3, 4}, {1, 2, 0, 4, 3}, {1, 2, 3, 0, 4}, {1, 2, 3, 4, 0}, {1, 2, 4, 0, 3},
{1, 2, 4, 3, 0}, {1, 3, 0, 2, 4}, {1, 3, 0, 4, 2}, {1, 3, 2, 0, 4}, {1, 3, 2, 4, 0}, {1, 3, 4, 0, 2},
{1, 3, 4, 2, 0}, {1, 4, 0, 2, 3}, {1, 4, 0, 3, 2}, {1, 4, 2, 0, 3}, {1, 4, 2, 3, 0}, {1, 4, 3, 0, 2},
{1, 4, 3, 2, 0}, {2, 0, 1, 3, 4}, {2, 0, 1, 4, 3}, {2, 0, 3, 1, 4}, {2, 0, 3, 4, 1}, {2, 0, 4, 1, 3},
{2, 0, 4, 3, 1}, {2, 1, 0, 3, 4}, {2, 1, 0, 4, 3}, {2, 1, 3, 0, 4}, {2, 1, 3, 4, 0}, {2, 1, 4, 0, 3},
{2, 1, 4, 3, 0}, {2, 3, 0, 1, 4}, {2, 3, 0, 4, 1}, {2, 3, 1, 0, 4}, {2, 3, 1, 4, 0}, {2, 3, 4, 0, 1},
{2, 3, 4, 1, 0}, {2, 4, 0, 1, 3}, {2, 4, 0, 3, 1}, {2, 4, 1, 0, 3}, {2, 4, 1, 3, 0}, {2, 4, 3, 0, 1},
{2, 4, 3, 1, 0}, {3, 0, 1, 2, 4}, {3, 0, 1, 4, 2}, {3, 0, 2, 1, 4}, {3, 0, 2, 4, 1}, {3, 0, 4, 1, 2},
{3, 0, 4, 2, 1}, {3, 1, 0, 2, 4}, {3, 1, 0, 4, 2}, {3, 1, 2, 0, 4}, {3, 1, 2, 4, 0}, {3, 1, 4, 0, 2},
{3, 1, 4, 2, 0}, {3, 2, 0, 1, 4}, {3, 2, 0, 4, 1}, {3, 2, 1, 0, 4}, {3, 2, 1, 4, 0}, {3, 2, 4, 0, 1},
{3, 2, 4, 1, 0}, {3, 4, 0, 1, 2}, {3, 4, 0, 2, 1}, {3, 4, 1, 0, 2}, {3, 4, 1, 2, 0}, {3, 4, 2, 0, 1},
{3, 4, 2, 1, 0}, {4, 0, 1, 2, 3}, {4, 0, 1, 3, 2}, {4, 0, 2, 1, 3}, {4, 0, 2, 3, 1}, {4, 0, 3, 1, 2},
{4, 0, 3, 2, 1}, {4, 1, 0, 2, 3}, {4, 1, 0, 3, 2}, {4, 1, 2, 0, 3}, {4, 1, 2, 3, 0}, {4, 1, 3, 0, 2},
{4, 1, 3, 2, 0}, {4, 2, 0, 1, 3}, {4, 2, 0, 3, 1}, {4, 2, 1, 0, 3}, {4, 2, 1, 3, 0}, {4, 2, 3, 0, 1},
{4, 2, 3, 1, 0}, {4, 3, 0, 1, 2}, {4, 3, 0, 2, 1}, {4, 3, 1, 0, 2}, {4, 3, 1, 2, 0}, {4, 3, 2, 0, 1},
{4, 3, 2, 1, 0}};
constexpr int perm6[][6] = {{0, 0, 1, 1, 2, 2}, {0, 0, 1, 2, 1, 2}, {0, 0, 1, 2, 2, 1}, {0, 0, 2, 1, 1, 2},
{0, 0, 2, 1, 2, 1}, {0, 0, 2, 2, 1, 1}, {0, 1, 0, 1, 2, 2}, {0, 1, 0, 2, 1, 2}, {0, 1, 0, 2, 2, 1},
{0, 1, 1, 0, 2, 2}, {0, 1, 1, 2, 0, 2}, {0, 1, 1, 2, 2, 0}, {0, 1, 2, 0, 1, 2}, {0, 1, 2, 0, 2, 1},
{0, 1, 2, 1, 0, 2}, {0, 1, 2, 1, 2, 0}, {0, 1, 2, 2, 0, 1}, {0, 1, 2, 2, 1, 0}, {0, 2, 0, 1, 1, 2},
{0, 2, 0, 1, 2, 1}, {0, 2, 0, 2, 1, 1}, {0, 2, 1, 0, 1, 2}, {0, 2, 1, 0, 2, 1}, {0, 2, 1, 1, 0, 2},
{0, 2, 1, 1, 2, 0}, {0, 2, 1, 2, 0, 1}, {0, 2, 1, 2, 1, 0}, {0, 2, 2, 0, 1, 1}, {0, 2, 2, 1, 0, 1},
{0, 2, 2, 1, 1, 0}, {1, 0, 0, 1, 2, 2}, {1, 0, 0, 2, 1, 2}, {1, 0, 0, 2, 2, 1}, {1, 0, 1, 0, 2, 2},
{1, 0, 1, 2, 0, 2}, {1, 0, 1, 2, 2, 0}, {1, 0, 2, 0, 1, 2}, {1, 0, 2, 0, 2, 1}, {1, 0, 2, 1, 0, 2},
{1, 0, 2, 1, 2, 0}, {1, 0, 2, 2, 0, 1}, {1, 0, 2, 2, 1, 0}, {1, 1, 0, 0, 2, 2}, {1, 1, 0, 2, 0, 2},
{1, 1, 0, 2, 2, 0}, {1, 1, 2, 0, 0, 2}, {1, 1, 2, 0, 2, 0}, {1, 1, 2, 2, 0, 0}, {1, 2, 0, 0, 1, 2},
{1, 2, 0, 0, 2, 1}, {1, 2, 0, 1, 0, 2}, {1, 2, 0, 1, 2, 0}, {1, 2, 0, 2, 0, 1}, {1, 2, 0, 2, 1, 0},
{1, 2, 1, 0, 0, 2}, {1, 2, 1, 0, 2, 0}, {1, 2, 1, 2, 0, 0}, {1, 2, 2, 0, 0, 1}, {1, 2, 2, 0, 1, 0},
{1, 2, 2, 1, 0, 0}, {2, 0, 0, 1, 1, 2}, {2, 0, 0, 1, 2, 1}, {2, 0, 0, 2, 1, 1}, {2, 0, 1, 0, 1, 2},
{2, 0, 1, 0, 2, 1}, {2, 0, 1, 1, 0, 2}, {2, 0, 1, 1, 2, 0}, {2, 0, 1, 2, 0, 1}, {2, 0, 1, 2, 1, 0},
{2, 0, 2, 0, 1, 1}, {2, 0, 2, 1, 0, 1}, {2, 0, 2, 1, 1, 0}, {2, 1, 0, 0, 1, 2}, {2, 1, 0, 0, 2, 1},
{2, 1, 0, 1, 0, 2}, {2, 1, 0, 1, 2, 0}, {2, 1, 0, 2, 0, 1}, {2, 1, 0, 2, 1, 0}, {2, 1, 1, 0, 0, 2},
{2, 1, 1, 0, 2, 0}, {2, 1, 1, 2, 0, 0}, {2, 1, 2, 0, 0, 1}, {2, 1, 2, 0, 1, 0}, {2, 1, 2, 1, 0, 0},
{2, 2, 0, 0, 1, 1}, {2, 2, 0, 1, 0, 1}, {2, 2, 0, 1, 1, 0}, {2, 2, 1, 0, 0, 1}, {2, 2, 1, 0, 1, 0},
{2, 2, 1, 1, 0, 0}};
constexpr int perm8[][8] = {{0, 0, 0, 0, 0, 0, 0, 0}};
struct int_wrapper {
int val = 10;
constexpr int_wrapper() = default;
constexpr int_wrapper(int x) : val{x} {}
constexpr int_wrapper(const int_wrapper&) = default;
constexpr int_wrapper(int_wrapper&& that) : val{exchange(that.val, -1)} {}
constexpr int_wrapper& operator=(const int_wrapper&) = default;
constexpr int_wrapper& operator=(int_wrapper&& that) {
val = exchange(that.val, -1);
return *this;
}
auto operator<=>(const int_wrapper&) const = default;
};
constexpr auto get_val = [](auto&& x) { return static_cast<const int_wrapper&>(x).val; };
template <auto& Expected>
struct next_perm_instantiator {
template <ranges::bidirectional_range R>
static constexpr void call() {
using ranges::next_permutation, ranges::next_permutation_result, ranges::equal, ranges::iterator_t;
constexpr auto number_of_permutations = static_cast<int>(ranges::size(Expected));
{ // Validate range overload
int_wrapper input[ranges::size(Expected[0])];
ranges::copy(Expected[0], input);
for (int i = 1; i < number_of_permutations; ++i) {
R range{input};
const same_as<next_permutation_result<iterator_t<R>>> auto result =
next_permutation(range, ranges::less{}, get_val);
assert(result.in == range.end());
assert(result.found);
assert(equal(input, Expected[i], ranges::equal_to{}, get_val));
}
R range{input};
const same_as<next_permutation_result<iterator_t<R>>> auto result =
next_permutation(range, ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
assert(equal(input, Expected[0]));
}
{ // Validate iterator overload
int_wrapper input[ranges::size(Expected[0])];
ranges::copy(Expected[0], input);
for (int i = 1; i < number_of_permutations; ++i) {
R range{input};
const same_as<next_permutation_result<iterator_t<R>>> auto result =
next_permutation(range.begin(), range.end(), ranges::less{}, get_val);
assert(result.in == range.end());
assert(result.found);
assert(equal(input, Expected[i]));
}
R range{input};
const same_as<next_permutation_result<iterator_t<R>>> auto result =
next_permutation(range.begin(), range.end(), ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
assert(equal(input, Expected[0]));
}
}
};
template <auto& Expected>
struct prev_perm_instantiator {
template <ranges::bidirectional_range R>
static constexpr void call() {
using ranges::prev_permutation, ranges::prev_permutation_result, ranges::equal, ranges::iterator_t;
constexpr auto number_of_permutations = static_cast<int>(ranges::size(Expected));
{ // Validate range overload
int_wrapper input[ranges::size(Expected[0])];
ranges::copy(Expected[number_of_permutations - 1], input);
for (int i = number_of_permutations - 1; i-- > 0;) {
R range{input};
const same_as<prev_permutation_result<iterator_t<R>>> auto result =
prev_permutation(range, ranges::less{}, get_val);
assert(result.in == range.end());
assert(result.found);
assert(equal(input, Expected[i]));
}
R range{input};
const same_as<prev_permutation_result<iterator_t<R>>> auto result =
prev_permutation(range, ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
assert(equal(input, Expected[number_of_permutations - 1]));
}
{ // Validate iterator overload
int_wrapper input[ranges::size(Expected[0])];
ranges::copy(Expected[number_of_permutations - 1], input);
for (int i = number_of_permutations - 1; i-- > 0;) {
R range{input};
const same_as<prev_permutation_result<iterator_t<R>>> auto result =
prev_permutation(range.begin(), range.end(), ranges::less{}, get_val);
assert(result.in == range.end());
assert(result.found);
assert(equal(input, Expected[i]));
}
R range{input};
const same_as<prev_permutation_result<iterator_t<R>>> auto result =
prev_permutation(range.begin(), range.end(), ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
assert(equal(input, Expected[number_of_permutations - 1]));
}
}
};
struct empty_range_test {
template <ranges::bidirectional_range R>
static constexpr void call() {
using ranges::next_permutation, ranges::next_permutation_result, ranges::prev_permutation,
ranges::prev_permutation_result, ranges::iterator_t;
{ // Validate range overload, next_permutation
R range{};
const same_as<next_permutation_result<iterator_t<R>>> auto result =
next_permutation(range, ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
}
{ // Validate iterator overload, next_permutation
R range{};
const same_as<next_permutation_result<iterator_t<R>>> auto result =
next_permutation(range.begin(), range.end(), ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
}
{ // Validate range overload, prev_permutation
R range{};
const same_as<prev_permutation_result<iterator_t<R>>> auto result =
prev_permutation(range, ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
}
{ // Validate iterator overload, prev_permutation
R range{};
const same_as<prev_permutation_result<iterator_t<R>>> auto result =
prev_permutation(range.begin(), range.end(), ranges::less{}, get_val);
assert(result.in == range.end());
assert(!result.found);
}
}
};
int main() {
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_bidi<empty_range_test, int_wrapper>(), true));
STATIC_ASSERT((test_bidi<next_perm_instantiator<perm1>, int_wrapper>(), true));
STATIC_ASSERT((test_bidi<next_perm_instantiator<perm4>, int_wrapper>(), true));
STATIC_ASSERT((test_bidi<next_perm_instantiator<perm8>, int_wrapper>(), true));
STATIC_ASSERT((test_bidi<prev_perm_instantiator<perm1>, int_wrapper>(), true));
STATIC_ASSERT((test_bidi<prev_perm_instantiator<perm4>, int_wrapper>(), true));
STATIC_ASSERT((test_bidi<prev_perm_instantiator<perm8>, int_wrapper>(), true));
#endif // TRANSITION, VSO-938163
test_bidi<empty_range_test, int_wrapper>();
test_bidi<next_perm_instantiator<perm1>, int_wrapper>();
test_bidi<next_perm_instantiator<perm4>, int_wrapper>();
test_bidi<next_perm_instantiator<perm5>, int_wrapper>();
test_bidi<next_perm_instantiator<perm6>, int_wrapper>();
test_bidi<next_perm_instantiator<perm8>, int_wrapper>();
test_bidi<prev_perm_instantiator<perm1>, int_wrapper>();
test_bidi<prev_perm_instantiator<perm4>, int_wrapper>();
test_bidi<prev_perm_instantiator<perm5>, int_wrapper>();
test_bidi<prev_perm_instantiator<perm6>, int_wrapper>();
test_bidi<prev_perm_instantiator<perm8>, int_wrapper>();
}