From 408cd46fb2f43357fdad40c17e173fc638649f0a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 7 Mar 2026 18:03:16 +0200 Subject: [PATCH 01/17] Test --- .../test.cpp | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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 727f1965045..196d8eaa099 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,30 @@ CONSTEXPR20 bool test_copy_part_2() { return true; } +CONSTEXPR20 bool test_is_permutation() { + const vector source = { + false, true, false, false, false, true, false, true, true, false, false, true, false, false, true, true}; + + // Actually a permutation + const vector perm = { + true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true}; + assert(is_permutation(source.begin(), source.end(), perm.begin())); + assert(is_permutation(source.begin(), source.end(), perm.begin(), perm.end())); + + // One extra true value + const vector extra_true = { + true, false, true, false, false, true, false, false, true, true, true, false, true, false, false, true}; + assert(!is_permutation(source.begin(), source.end(), extra_true.begin())); + assert(!is_permutation(source.begin(), source.end(), extra_true.begin(), extra_true.end())); + + // One element longer + const vector longer = { + true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true, false}; + assert(!is_permutation(source.begin(), source.end(), longer.begin(), longer.end())); + + 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 +1734,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 +1746,7 @@ int main() { test_meow_of(); test_copy_part_1(); test_copy_part_2(); + test_is_permutation(); test_huge_vector_bool(); From 90e156c4da97056396cb39ef8800606fb1900c8d Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 7 Mar 2026 18:09:34 +0200 Subject: [PATCH 02/17] Benchmark --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/vector_bool_permute.cpp | 44 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 benchmarks/src/vector_bool_permute.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index ad9b1f0a436..6b59ee804ff 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 00000000000..e1182d2b09c --- /dev/null +++ b/benchmarks/src/vector_bool_permute.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +// +#include +#include +#include +#include + +#include "utility.hpp" + +using namespace std; + +void perm_check_3(benchmark::State& state) { + const auto size = static_cast(state.range(0)); + vector v1 = random_vector(size); + vector v2 = random_vector(size, 1u); + + for (auto _ : state) { + benchmark::DoNotOptimize(v1); + benchmark::DoNotOptimize(v2); + bool r = is_permutation(v1.begin(), v1.end(), v2.begin()); + benchmark::DoNotOptimize(r); + } +} + +void perm_check_4(benchmark::State& state) { + const auto size = static_cast(state.range(0)); + vector v1 = random_vector(size); + vector v2 = random_vector(size, 1u); + + for (auto _ : state) { + benchmark::DoNotOptimize(v1); + benchmark::DoNotOptimize(v2); + bool r = is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end()); + benchmark::DoNotOptimize(r); + } +} + +BENCHMARK(perm_check_3)->RangeMultiplier(64)->Range(64, 64 << 10); +BENCHMARK(perm_check_4)->RangeMultiplier(64)->Range(64, 64 << 10); + +BENCHMARK_MAIN(); From bf9ff31ece5bee76d62fd010d2b98fccf87596bb Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 7 Mar 2026 18:19:18 +0200 Subject: [PATCH 03/17] Mutation! --- stl/inc/algorithm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index b925caf9900..50792c5edaf 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1260,6 +1260,16 @@ _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_vb_iterator && _Is_vb_iterator + && _Is_any_of_v<_Pr, +#if _HAS_CXX20 + _RANGES equal_to, +#endif + equal_to<>, equal_to>) { + return _STD _Count_vbool(_UFirst1, _ULast1, true) + == _STD _Count_vbool(_UFirst2, _UFirst2 + (_ULast1 - _UFirst1), true); + } + for (;; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix if (_UFirst1 == _ULast1) { // everything matched return true; @@ -1297,6 +1307,15 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( return false; } + if constexpr (_Is_vb_iterator && _Is_vb_iterator + && _Is_any_of_v<_Pr, +#if _HAS_CXX20 + _RANGES equal_to, +#endif + equal_to<>, equal_to>) { + return _STD _Count_vbool(_UFirst1, _ULast1, true) == _STD _Count_vbool(_UFirst2, _ULast2, true); + } + for (; _UFirst1 != _ULast1; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix if (!_Pred(*_UFirst1, *_UFirst2)) { // found first inequality, check match counts in suffix From 549a9d3b4889071646f020049895e550a72c5e57 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 7 Mar 2026 19:53:24 +0200 Subject: [PATCH 04/17] Generalize --- stl/inc/algorithm | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 50792c5edaf..b3e8e591acb 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1260,14 +1260,14 @@ _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_vb_iterator && _Is_vb_iterator + 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 _Count_vbool(_UFirst1, _ULast1, true) - == _STD _Count_vbool(_UFirst2, _UFirst2 + (_ULast1 - _UFirst1), true); + return _STD count(_UFirst1, _ULast1, true) == _STD count(_UFirst2, _UFirst2 + (_ULast1 - _UFirst1), true); } for (;; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix @@ -1307,13 +1307,14 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( return false; } - if constexpr (_Is_vb_iterator && _Is_vb_iterator + 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 _Count_vbool(_UFirst1, _ULast1, true) == _STD _Count_vbool(_UFirst2, _ULast2, true); + return _STD count(_UFirst1, _ULast1, true) == _STD count(_UFirst2, _ULast2, true); } for (; _UFirst1 != _ULast1; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix From 5a0ba816377c67a2b06d88d16f18dd64c2cc7719 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 8 Mar 2026 09:42:40 +0200 Subject: [PATCH 05/17] benchmark plain dynamic array --- benchmarks/src/vector_bool_permute.cpp | 57 ++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/benchmarks/src/vector_bool_permute.cpp b/benchmarks/src/vector_bool_permute.cpp index e1182d2b09c..73ef9bafa24 100644 --- a/benchmarks/src/vector_bool_permute.cpp +++ b/benchmarks/src/vector_bool_permute.cpp @@ -12,33 +12,74 @@ using namespace std; -void perm_check_3(benchmark::State& state) { +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 = random_vector(size, 1u); + 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 = is_permutation(v1.begin(), v1.end(), v2.begin()); + 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); } } -void perm_check_4(benchmark::State& state) { + +template +void perm_vbool_check(benchmark::State& state) { const auto size = static_cast(state.range(0)); vector v1 = random_vector(size); - vector v2 = random_vector(size, 1u); + 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 = is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end()); + 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_check_3)->RangeMultiplier(64)->Range(64, 64 << 10); -BENCHMARK(perm_check_4)->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_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(); From 443e021073fe965b988f3cb259a136aac4a6e9d6 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 8 Mar 2026 10:33:43 +0200 Subject: [PATCH 06/17] Restore mismatch --- stl/inc/algorithm | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index b3e8e591acb..dd32dd766d6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1267,6 +1267,16 @@ _NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _Fw _RANGES equal_to, #endif equal_to<>, equal_to>) { + if constexpr (!_Is_vb_iterator && !_Is_vb_iterator) { + auto _Pair = _STD mismatch(_UFirst1, _ULast1, _UFirst2); + _UFirst1 = _STD move(_Pair.first); + _UFirst2 = _STD move(_Pair.second); + + if (_UFirst1 == _ULast1) { + return true; + } + } + return _STD count(_UFirst1, _ULast1, true) == _STD count(_UFirst2, _UFirst2 + (_ULast1 - _UFirst1), true); } @@ -1314,6 +1324,16 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( _RANGES equal_to, #endif equal_to<>, equal_to>) { + if constexpr (!_Is_vb_iterator && !_Is_vb_iterator) { + auto _Pair = _STD mismatch(_UFirst1, _ULast1, _UFirst2); + _UFirst1 = _STD move(_Pair.first); + _UFirst2 = _STD move(_Pair.second); + + if (_UFirst1 == _ULast1) { + return true; + } + } + return _STD count(_UFirst1, _ULast1, true) == _STD count(_UFirst2, _ULast2, true); } From 08b26573a4607aebd1959be5988e07a80342e89c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 8 Mar 2026 10:57:24 +0200 Subject: [PATCH 07/17] format --- benchmarks/src/vector_bool_permute.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/benchmarks/src/vector_bool_permute.cpp b/benchmarks/src/vector_bool_permute.cpp index 73ef9bafa24..a02ef61c5a2 100644 --- a/benchmarks/src/vector_bool_permute.cpp +++ b/benchmarks/src/vector_bool_permute.cpp @@ -53,9 +53,7 @@ void perm_vbool_check(benchmark::State& state) { vector v2; if constexpr (Eq == equality::eq) { v2 = v1; - } - else - { + } else { v2 = random_vector(size, 1u); } From 4ca22be012d0f02a92d56fc404d82860bc95b982 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 8 Mar 2026 11:56:15 +0200 Subject: [PATCH 08/17] Admit we rely on random access iterators, extract --- stl/inc/algorithm | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index dd32dd766d6..2757143f685 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, ...) @@ -1261,23 +1276,15 @@ _NODISCARD _CONSTEXPR20 bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1, _Fw 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_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>) { - if constexpr (!_Is_vb_iterator && !_Is_vb_iterator) { - auto _Pair = _STD mismatch(_UFirst1, _ULast1, _UFirst2); - _UFirst1 = _STD move(_Pair.first); - _UFirst2 = _STD move(_Pair.second); - - if (_UFirst1 == _ULast1) { - return true; - } - } - - return _STD count(_UFirst1, _ULast1, true) == _STD count(_UFirst2, _UFirst2 + (_ULast1 - _UFirst1), true); + return _STD _Is_permutation_of_bool(_UFirst1, _ULast1, _UFirst2, _UFirst2 + (_ULast1 - _UFirst1)); } for (;; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix @@ -1324,17 +1331,7 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( _RANGES equal_to, #endif equal_to<>, equal_to>) { - if constexpr (!_Is_vb_iterator && !_Is_vb_iterator) { - auto _Pair = _STD mismatch(_UFirst1, _ULast1, _UFirst2); - _UFirst1 = _STD move(_Pair.first); - _UFirst2 = _STD move(_Pair.second); - - if (_UFirst1 == _ULast1) { - return true; - } - } - - return _STD count(_UFirst1, _ULast1, true) == _STD count(_UFirst2, _ULast2, true); + return _STD _Is_permutation_of_bool(_UFirst1, _ULast1, _UFirst2, _ULast2); } for (; _UFirst1 != _ULast1; ++_UFirst1, (void) ++_UFirst2) { // trim matching prefix From 21888e27885a6b94e8d8f858e79126e9c68fb8f1 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 8 Mar 2026 12:18:18 +0200 Subject: [PATCH 09/17] Expand coverage --- .../test.cpp | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) 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 196d8eaa099..67cab7cfb7f 100644 --- a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -1519,25 +1519,44 @@ CONSTEXPR20 bool test_copy_part_2() { } CONSTEXPR20 bool test_is_permutation() { - const vector source = { + 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 - const vector perm = { + constexpr bool perm_raw[] = { true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true}; - assert(is_permutation(source.begin(), source.end(), perm.begin())); - assert(is_permutation(source.begin(), source.end(), perm.begin(), perm.end())); + 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 - const vector extra_true = { + constexpr bool extra_true_raw[] = { true, false, true, false, false, true, false, false, true, true, true, false, true, false, false, true}; - assert(!is_permutation(source.begin(), source.end(), extra_true.begin())); - assert(!is_permutation(source.begin(), source.end(), extra_true.begin(), extra_true.end())); + 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 - const vector longer = { + constexpr bool longer_raw[] = { true, false, true, false, false, true, false, false, true, false, true, false, true, false, false, true, false}; - assert(!is_permutation(source.begin(), source.end(), longer.begin(), longer.end())); + 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; } From b7675088b178bdfe0093245efab167a1d5ade7e0 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 29 Apr 2026 16:41:40 +0300 Subject: [PATCH 10/17] copypasta error --- benchmarks/src/vector_bool_permute.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/vector_bool_permute.cpp b/benchmarks/src/vector_bool_permute.cpp index a02ef61c5a2..b9beddab12b 100644 --- a/benchmarks/src/vector_bool_permute.cpp +++ b/benchmarks/src/vector_bool_permute.cpp @@ -33,8 +33,8 @@ void perm_arr_check(benchmark::State& state) { copy(v2.begin(), v2.end(), a2.get()); for (auto _ : state) { - benchmark::DoNotOptimize(v1); - benchmark::DoNotOptimize(v2); + benchmark::DoNotOptimize(a1.get()); + benchmark::DoNotOptimize(a2.get()); bool r; if constexpr (Args == args::three) { r = is_permutation(a1.get(), a1.get() + size, a2.get()); From d01fcda34da17ef6b6c611331bdfd6e5ba692b9c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 30 May 2026 20:41:53 +0300 Subject: [PATCH 11/17] Ok let's check the unwrapped iterators --- stl/inc/algorithm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index be2afbf0c30..1cf8fc12eb9 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1337,7 +1337,7 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( auto _ULast1 = _STD _Get_unwrapped(_Last1); auto _UFirst2 = _STD _Get_unwrapped(_First2); auto _ULast2 = _STD _Get_unwrapped(_Last2); - if constexpr (_Is_ranges_random_iter_v<_FwdIt1> && _Is_ranges_random_iter_v<_FwdIt2>) { + if constexpr (_Is_ranges_random_iter_v && _Is_ranges_random_iter_v) { if (_ULast1 - _UFirst1 != _ULast2 - _UFirst2) { return false; } From f321214300816d48608181641cd66354e6bd5320 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 11 Aug 2026 11:46:56 +0300 Subject: [PATCH 12/17] evil mix test --- .../test.cpp | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) 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 67cab7cfb7f..a7dcd75c39f 100644 --- a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -1561,6 +1561,114 @@ CONSTEXPR20 bool test_is_permutation() { return true; } +void test_is_permutation_vbool_like_iterators() { + // vector like proxy reference class with evil behavior + struct vbool_like_reference { + const bool* ptr; + + operator bool() const { + return *ptr; + } + + // proxy-to-proxy comparison; the evil part, not present in vector proxies + bool operator==(const vbool_like_reference&) const { + return true; // all proxies are equal + } + + // proxy-to-other-proxy comparison; the evil part, not present in vector proxies + bool operator==(const vector::reference&) const { + return true; // all proxies are equal + } + }; + + struct vbool_like_iterator { + using iterator_category = random_access_iterator_tag; + using value_type = bool; + using difference_type = ptrdiff_t; + using pointer = bool*; + using reference = vbool_like_reference; + + const bool* ptr; + + vbool_like_reference operator*() const { + return {ptr}; + } + + vbool_like_iterator& operator++() { + ++ptr; + return *this; + } + + vbool_like_iterator& operator--() { + --ptr; + return *this; + } + + vbool_like_iterator& operator+=(ptrdiff_t d) { + ptr += d; + return *this; + } + + vbool_like_iterator& operator-=(ptrdiff_t d) { + ptr -= d; + return *this; + } + + vbool_like_iterator operator+(ptrdiff_t d) const { + return {ptr + d}; + } + + vbool_like_iterator operator-(ptrdiff_t d) const { + return {ptr - d}; + } + + ptrdiff_t operator-(const vbool_like_iterator& other) const { + return ptr - other.ptr; + } + + bool operator==(const vbool_like_iterator& other) const { + return ptr == other.ptr; + } + + bool operator!=(const vbool_like_iterator& other) const { + return ptr != other.ptr; + } + + bool operator<(const vbool_like_iterator& other) const { + return ptr < other.ptr; + } + + bool operator<=(const vbool_like_iterator& other) const { + return ptr <= other.ptr; + } + + bool operator>(const vbool_like_iterator& other) const { + return ptr > other.ptr; + } + + bool operator>=(const vbool_like_iterator& other) const { + return ptr >= other.ptr; + } + }; + + bool all_true[] = {true, true, true}; + vector some_true{true, false, true}; + + const vbool_like_iterator all_true_begin{begin(all_true)}; + const vbool_like_iterator all_true_end{end(all_true)}; + + // different true count + assert(count(all_true_begin, all_true_end, true) == 3); + assert(count(some_true.begin(), some_true.end(), true) == 2); + + // but still equivalent + assert(equal(all_true_begin, all_true_end, some_true.begin(), some_true.end())); + assert(mismatch(all_true_begin, all_true_end, some_true.begin(), some_true.end()).first == all_true_end); + + // now these are permutations due to equivalence + assert(is_permutation(all_true_begin, all_true_end, some_true.begin(), some_true.end())); +} + void initialize_randomness(mt19937_64& gen) { constexpr size_t n = mt19937_64::state_size; constexpr size_t w = mt19937_64::word_size; @@ -1766,6 +1874,7 @@ int main() { test_copy_part_1(); test_copy_part_2(); test_is_permutation(); + test_is_permutation_vbool_like_iterators(); test_huge_vector_bool(); From b0279705afa2412d49a356cf0318b37bdb329c12 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 11 Aug 2026 12:12:20 +0300 Subject: [PATCH 13/17] evil mix fix --- stl/inc/algorithm | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 1cf8fc12eb9..9c3af3efd19 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1270,6 +1270,12 @@ namespace ranges { } // namespace ranges #endif // _HAS_CXX20 +template +constexpr bool _Can_optimize_permutation_of_bool_iter = + _Is_vb_iterator<_It> + || (is_same_v<_Iter_value_t<_It>, bool> && is_same_v<_Remove_cvref_t<_Iter_ref_t<_It>>, bool> + && _Is_ranges_random_iter_v<_It>); + 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>) { @@ -1293,10 +1299,8 @@ _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 + if constexpr (_Can_optimize_permutation_of_bool_iter + && _Can_optimize_permutation_of_bool_iter && _Is_any_of_v<_Pr, #if _HAS_CXX20 _RANGES equal_to, @@ -1342,8 +1346,8 @@ _NODISCARD _CONSTEXPR20 bool is_permutation( return false; } - if constexpr (is_same_v<_Iter_value_t, bool> - && is_same_v<_Iter_value_t, bool> + if constexpr (_Can_optimize_permutation_of_bool_iter + && _Can_optimize_permutation_of_bool_iter && _Is_any_of_v<_Pr, #if _HAS_CXX20 _RANGES equal_to, From a3390f8366ad4e9ca85d291e5d1de202439236ee Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 8 Sep 2026 08:56:23 +0300 Subject: [PATCH 14/17] deterministic mismatch --- benchmarks/src/vector_bool_permute.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/benchmarks/src/vector_bool_permute.cpp b/benchmarks/src/vector_bool_permute.cpp index b9beddab12b..1bdbf30f109 100644 --- a/benchmarks/src/vector_bool_permute.cpp +++ b/benchmarks/src/vector_bool_permute.cpp @@ -20,11 +20,9 @@ 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); + vector v2 = v1; + if constexpr (Eq == equality::neq) { + v2[v2.size() / 2].flip(); } auto a1 = make_unique(size); @@ -50,11 +48,9 @@ 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); + vector v2 = v1; + if constexpr (Eq == equality::neq) { + v2[v2.size() / 2].flip(); } for (auto _ : state) { From 06cedb7c799fedb1dd948abeb0aaae65d5bf8333 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 8 Sep 2026 09:04:59 +0300 Subject: [PATCH 15/17] missing iterator ops --- .../test.cpp | 160 ++++++++++-------- 1 file changed, 90 insertions(+), 70 deletions(-) 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 a7dcd75c39f..24c3c6cc859 100644 --- a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -1561,96 +1561,116 @@ CONSTEXPR20 bool test_is_permutation() { return true; } -void test_is_permutation_vbool_like_iterators() { - // vector like proxy reference class with evil behavior - struct vbool_like_reference { - const bool* ptr; +// vector like proxy reference class with evil behavior +struct vbool_like_reference { + const bool* ptr; - operator bool() const { - return *ptr; - } + operator bool() const { + return *ptr; + } - // proxy-to-proxy comparison; the evil part, not present in vector proxies - bool operator==(const vbool_like_reference&) const { - return true; // all proxies are equal - } + // proxy-to-proxy comparison; the evil part, not present in vector proxies + bool operator==(const vbool_like_reference&) const { + return true; // all proxies are equal + } - // proxy-to-other-proxy comparison; the evil part, not present in vector proxies - bool operator==(const vector::reference&) const { - return true; // all proxies are equal - } - }; + // proxy-to-other-proxy comparison; the evil part, not present in vector proxies + bool operator==(const vector::reference&) const { + return true; // all proxies are equal + } +}; - struct vbool_like_iterator { - using iterator_category = random_access_iterator_tag; - using value_type = bool; - using difference_type = ptrdiff_t; - using pointer = bool*; - using reference = vbool_like_reference; +struct vbool_like_iterator { + using iterator_category = random_access_iterator_tag; + using value_type = bool; + using difference_type = ptrdiff_t; + using pointer = bool*; + using reference = vbool_like_reference; - const bool* ptr; + const bool* ptr; - vbool_like_reference operator*() const { - return {ptr}; - } + vbool_like_reference operator*() const { + return {ptr}; + } - vbool_like_iterator& operator++() { - ++ptr; - return *this; - } + vbool_like_iterator& operator++() { + ++ptr; + return *this; + } - vbool_like_iterator& operator--() { - --ptr; - return *this; - } + vbool_like_iterator& operator--() { + --ptr; + return *this; + } - vbool_like_iterator& operator+=(ptrdiff_t d) { - ptr += d; - return *this; - } + vbool_like_iterator operator++(int) { + vbool_like_iterator result; + ++ptr; + return result; + } - vbool_like_iterator& operator-=(ptrdiff_t d) { - ptr -= d; - return *this; - } + vbool_like_iterator operator--(int) { + vbool_like_iterator result; + --ptr; + return result; + } - vbool_like_iterator operator+(ptrdiff_t d) const { - return {ptr + d}; - } + vbool_like_iterator& operator+=(const ptrdiff_t d) { + ptr += d; + return *this; + } - vbool_like_iterator operator-(ptrdiff_t d) const { - return {ptr - d}; - } + vbool_like_iterator& operator-=(const ptrdiff_t d) { + ptr -= d; + return *this; + } - ptrdiff_t operator-(const vbool_like_iterator& other) const { - return ptr - other.ptr; - } + vbool_like_iterator operator+(const ptrdiff_t d) const { + return {ptr + d}; + } - bool operator==(const vbool_like_iterator& other) const { - return ptr == other.ptr; - } + vbool_like_iterator operator-(const ptrdiff_t d) const { + return {ptr - d}; + } - bool operator!=(const vbool_like_iterator& other) const { - return ptr != other.ptr; - } + vbool_like_reference operator[](const ptrdiff_t d) const { + return {ptr - d}; + } - bool operator<(const vbool_like_iterator& other) const { - return ptr < other.ptr; - } + ptrdiff_t operator-(const vbool_like_iterator& other) const { + return ptr - other.ptr; + } - bool operator<=(const vbool_like_iterator& other) const { - return ptr <= other.ptr; - } + bool operator==(const vbool_like_iterator& other) const { + return ptr == other.ptr; + } - bool operator>(const vbool_like_iterator& other) const { - return ptr > other.ptr; - } + bool operator!=(const vbool_like_iterator& other) const { + return ptr != other.ptr; + } - bool operator>=(const vbool_like_iterator& other) const { - return ptr >= other.ptr; - } - }; + bool operator<(const vbool_like_iterator& other) const { + return ptr < other.ptr; + } + + bool operator<=(const vbool_like_iterator& other) const { + return ptr <= other.ptr; + } + bool operator>(const vbool_like_iterator& other) const { + return ptr > other.ptr; + } + + bool operator>=(const vbool_like_iterator& other) const { + return ptr >= other.ptr; + } +}; + +vbool_like_iterator operator+(const ptrdiff_t d, const vbool_like_iterator& i) { + return {d + i.ptr}; +} + +void test_is_permutation_vbool_like_iterators() { bool all_true[] = {true, true, true}; vector some_true{true, false, true}; From 96dfc8627f02e8853610c574bb0195b5cae5bdf9 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 8 Sep 2026 09:17:29 +0300 Subject: [PATCH 16/17] Good catch Copilot, applying (except AAA style) --- tests/std/tests/GH_000625_vector_bool_optimization/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 24c3c6cc859..9b9b474ceec 100644 --- a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -1604,13 +1604,13 @@ struct vbool_like_iterator { } vbool_like_iterator operator++(int) { - vbool_like_iterator result; + const vbool_like_iterator result = *this; ++ptr; return result; } vbool_like_iterator operator--(int) { - vbool_like_iterator result; + const vbool_like_iterator result = *this; --ptr; return result; } From 2153aaea936b8869e5d73953a07b2c36aae8c36b Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 8 Sep 2026 09:25:01 +0300 Subject: [PATCH 17/17] Oops wrong direction --- tests/std/tests/GH_000625_vector_bool_optimization/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9b9b474ceec..f24aad769b6 100644 --- a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -1634,7 +1634,7 @@ struct vbool_like_iterator { } vbool_like_reference operator[](const ptrdiff_t d) const { - return {ptr - d}; + return {ptr + d}; } ptrdiff_t operator-(const vbool_like_iterator& other) const {