diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index ad9b1f0a43..6b59ee804f 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -146,4 +146,5 @@ add_benchmark(vector_bool_copy_n src/vector_bool_copy_n.cpp) add_benchmark(vector_bool_count src/vector_bool_count.cpp) add_benchmark(vector_bool_meow_of src/vector_bool_meow_of.cpp) add_benchmark(vector_bool_move src/vector_bool_move.cpp) +add_benchmark(vector_bool_permute src/vector_bool_permute.cpp) add_benchmark(vector_bool_transform src/vector_bool_transform.cpp) diff --git a/benchmarks/src/vector_bool_permute.cpp b/benchmarks/src/vector_bool_permute.cpp new file mode 100644 index 0000000000..a02ef61c5a --- /dev/null +++ b/benchmarks/src/vector_bool_permute.cpp @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +// +#include +#include +#include +#include + +#include "utility.hpp" + +using namespace std; + +enum class equality { eq, neq }; +enum class args { three, four }; + + +template +void perm_arr_check(benchmark::State& state) { + const auto size = static_cast(state.range(0)); + vector v1 = random_vector(size); + vector v2; + if constexpr (Eq == equality::eq) { + v2 = v1; + } else { + v2 = random_vector(size, 1u); + } + + auto a1 = make_unique(size); + auto a2 = make_unique(size); + copy(v1.begin(), v1.end(), a1.get()); + copy(v2.begin(), v2.end(), a2.get()); + + for (auto _ : state) { + benchmark::DoNotOptimize(v1); + benchmark::DoNotOptimize(v2); + bool r; + if constexpr (Args == args::three) { + r = is_permutation(a1.get(), a1.get() + size, a2.get()); + } else { + r = is_permutation(a1.get(), a1.get() + size, a2.get(), a2.get() + size); + } + benchmark::DoNotOptimize(r); + } +} + + +template +void perm_vbool_check(benchmark::State& state) { + const auto size = static_cast(state.range(0)); + vector v1 = random_vector(size); + vector v2; + if constexpr (Eq == equality::eq) { + v2 = v1; + } else { + v2 = random_vector(size, 1u); + } + + for (auto _ : state) { + benchmark::DoNotOptimize(v1); + benchmark::DoNotOptimize(v2); + bool r; + if constexpr (Args == args::three) { + r = is_permutation(v1.begin(), v1.end(), v2.begin()); + } else { + r = is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end()); + } + benchmark::DoNotOptimize(r); + } +} + +BENCHMARK(perm_arr_check)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_arr_check)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_arr_check)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_arr_check)->RangeMultiplier(64)->Range(64, 64 << 10); + +BENCHMARK(perm_vbool_check)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_vbool_check)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_vbool_check)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_vbool_check)->RangeMultiplier(64)->Range(64, 64 << 10); + +BENCHMARK_MAIN(); diff --git a/stl/inc/algorithm b/stl/inc/algorithm index b925caf990..2757143f68 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1252,6 +1252,21 @@ namespace ranges { } // namespace ranges #endif // _HAS_CXX20 +template +_NODISCARD _CONSTEXPR20 bool _Is_permutation_of_bool(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2) { + if constexpr (!_Is_vb_iterator<_FwdIt1> && !_Is_vb_iterator<_FwdIt2>) { + auto _Pair = _STD mismatch(_First1, _Last1, _First2); + _First1 = _STD move(_Pair.first); + _First2 = _STD move(_Pair.second); + + if (_First1 == _Last1) { + return true; + } + } + + return _STD count(_First1, _Last1, true) == _STD count(_First2, _Last2, true); +} + _EXPORT_STD template _NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Pr _Pred) { // test if [_First1, _Last1) == permuted [_First2, ...) @@ -1260,6 +1275,18 @@ _NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _Fw const auto _ULast1 = _STD _Get_unwrapped(_Last1); auto _UFirst2 = _STD _Get_unwrapped_n(_First2, _STD _Idl_distance<_FwdIt1>(_UFirst1, _ULast1)); + if constexpr (is_same_v<_Iter_value_t, bool> + && is_same_v<_Iter_value_t, bool> // + && _Is_ranges_random_iter_v // + && _Is_ranges_random_iter_v + && _Is_any_of_v<_Pr, +#if _HAS_CXX20 + _RANGES equal_to, +#endif + equal_to<>, equal_to>) { + return _STD _Is_permutation_of_bool(_UFirst1, _ULast1, _UFirst2, _UFirst2 + (_ULast1 - _UFirst1)); + } + for (;; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix if (_UFirst1 == _ULast1) { // everything matched return true; @@ -1297,6 +1324,16 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( return false; } + if constexpr (is_same_v<_Iter_value_t, bool> + && is_same_v<_Iter_value_t, bool> + && _Is_any_of_v<_Pr, +#if _HAS_CXX20 + _RANGES equal_to, +#endif + equal_to<>, equal_to>) { + return _STD _Is_permutation_of_bool(_UFirst1, _ULast1, _UFirst2, _ULast2); + } + for (; _UFirst1 != _ULast1; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix if (!_Pred(*_UFirst1, *_UFirst2)) { // found first inequality, check match counts in suffix diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp index 727f196504..67cab7cfb7 100644 --- a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -1518,6 +1518,49 @@ CONSTEXPR20 bool test_copy_part_2() { return true; } +CONSTEXPR20 bool test_is_permutation() { + constexpr bool perm_source_raw[] = { + false, true, false, false, false, true, false, true, true, false, false, true, false, false, true, true}; + const vector perm_source(begin(perm_source_raw), end(perm_source_raw)); + + // Actually a permutation + constexpr bool perm_raw[] = { + true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true}; + const vector perm(begin(perm_raw), end(perm_raw)); + assert(is_permutation(perm_source.begin(), perm_source.end(), perm.begin())); + assert(is_permutation(perm_source.begin(), perm_source.end(), perm.begin(), perm.end())); + assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), perm.begin())); + assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), perm.begin(), perm.end())); + assert(is_permutation(perm_source.begin(), perm_source.end(), begin(perm_raw))); + assert(is_permutation(perm_source.begin(), perm_source.end(), begin(perm_raw), end(perm_raw))); + assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(perm_raw))); + assert(is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(perm_raw), end(perm_raw))); + + // One extra true value + constexpr bool extra_true_raw[] = { + true, false, true, false, false, true, false, false, true, true, true, false, true, false, false, true}; + const vector extra_true(begin(extra_true_raw), end(extra_true_raw)); + assert(!is_permutation(perm_source.begin(), perm_source.end(), extra_true.begin())); + assert(!is_permutation(perm_source.begin(), perm_source.end(), extra_true.begin(), extra_true.end())); + assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), extra_true.begin())); + assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), extra_true.begin(), extra_true.end())); + assert(!is_permutation(perm_source.begin(), perm_source.end(), begin(extra_true_raw))); + assert(!is_permutation(perm_source.begin(), perm_source.end(), begin(extra_true_raw), end(extra_true_raw))); + assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(extra_true_raw))); + assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(extra_true_raw), end(extra_true_raw))); + + // One element longer + constexpr bool longer_raw[] = { + true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true, false}; + const vector longer(begin(longer_raw), end(longer_raw)); + assert(!is_permutation(perm_source.begin(), perm_source.end(), longer.begin(), longer.end())); + assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), longer.begin(), longer.end())); + assert(!is_permutation(perm_source.begin(), perm_source.end(), begin(longer_raw), end(longer_raw))); + assert(!is_permutation(begin(perm_source_raw), end(perm_source_raw), begin(longer_raw), end(longer_raw))); + + return true; +} + void initialize_randomness(mt19937_64& gen) { constexpr size_t n = mt19937_64::state_size; constexpr size_t w = mt19937_64::word_size; @@ -1710,6 +1753,7 @@ static_assert(test_meow_of()); #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-2574489 static_assert(test_copy_part_1()); static_assert(test_copy_part_2()); +static_assert(test_is_permutation()); #endif // ^^^ no workaround ^^^ #endif // _HAS_CXX20 @@ -1721,6 +1765,7 @@ int main() { test_meow_of(); test_copy_part_1(); test_copy_part_2(); + test_is_permutation(); test_huge_vector_bool();