diff --git a/SG14/slot_map.h b/SG14/slot_map.h index cb3cb839..51cb6a81 100644 --- a/SG14/slot_map.h +++ b/SG14/slot_map.h @@ -240,6 +240,40 @@ class slot_map return 1; } + template + constexpr iterator partition(const Pred& pred) { + iterator it = this->begin(); + iterator jt = this->end(); + if (it == jt) return it; + while (true) { + --jt; + if (it == jt) return it; + while (pred(*it)) { + ++it; + if (it == jt) return it; + } + while (!pred(*jt)) { + --jt; + if (it == jt) return it; + } + // Swap *it and *jt in the underlying container, + // but then fix up their keys so they don't appear to move. + auto it_value_index = std::distance(values_.begin(), it); + auto it_reversemap_iter = std::next(reverse_map_.begin(), it_value_index); + auto it_slot_iter = std::next(slots_.begin(), *it_reversemap_iter); + auto jt_value_index = std::distance(values_.begin(), jt); + auto jt_reversemap_iter = std::next(reverse_map_.begin(), jt_value_index); + auto jt_slot_iter = std::next(slots_.begin(), *jt_reversemap_iter); + + using std::swap; + swap(*it, *jt); + swap(*it_slot_iter, *jt_slot_iter); + swap(*it_reversemap_iter, *jt_reversemap_iter); + ++it; + if (it == jt) return it; + } + } + // clear() has O(n) time complexity and O(1) space complexity. // It also has semantics differing from erase(begin(), end()) // in that it also resets the generation counter of every slot diff --git a/SG14_test/slot_map_test.cpp b/SG14_test/slot_map_test.cpp index 9dacaee8..cbae8c4c 100644 --- a/SG14_test/slot_map_test.cpp +++ b/SG14_test/slot_map_test.cpp @@ -278,6 +278,39 @@ static void EraseRangeTest() } } +template +static void PartitionTest() +{ + using T = typename SM::mapped_type; + SM sm; + auto key3 = sm.insert(Monad::from_value(3)); + auto key1 = sm.insert(Monad::from_value(1)); + auto key4 = sm.insert(Monad::from_value(4)); + auto key5 = sm.insert(Monad::from_value(5)); + auto key9 = sm.insert(Monad::from_value(9)); + auto key2 = sm.insert(Monad::from_value(2)); + auto key6 = sm.insert(Monad::from_value(6)); + + auto pivot = sm.partition([](const auto& elt) { + return Monad::value_of(elt) >= 5; + }); + + for (auto it = sm.begin(); it != pivot; ++it) { + assert(Monad::value_of(*it) >= 5); + } + for (auto it = pivot; it != sm.end(); ++it) { + assert(Monad::value_of(*it) < 5); + } + + assert(Monad::value_of(*sm.find(key3)) == 3); + assert(Monad::value_of(*sm.find(key1)) == 1); + assert(Monad::value_of(*sm.find(key4)) == 4); + assert(Monad::value_of(*sm.find(key5)) == 5); + assert(Monad::value_of(*sm.find(key9)) == 9); + assert(Monad::value_of(*sm.find(key2)) == 2); + assert(Monad::value_of(*sm.find(key6)) == 6); +} + static void TypedefTests() { if (true) { @@ -385,6 +418,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=3]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); // Test slot_map with a custom key type (C++14 destructuring). using slot_map_2 = stdext::slot_map; @@ -393,6 +427,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=5]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); #if __cplusplus >= 201703L // Test slot_map with a custom key type (C++17 destructuring). @@ -402,6 +437,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=3]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); #endif // __cplusplus >= 201703L // Test slot_map with a custom (but standard and random-access) container type. @@ -411,6 +447,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=7]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); // Test slot_map with a custom (non-standard, random-access) container type. using slot_map_5 = stdext::slot_map, TestContainer::Vector>; @@ -419,6 +456,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=7]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); // Test slot_map with a custom (standard, bidirectional-access) container type. using slot_map_6 = stdext::slot_map, std::list>; @@ -427,6 +465,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=7]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); // Test slot_map with a move-only value_type. // Sadly, standard containers do not propagate move-only-ness, so we must use our custom Vector instead. @@ -440,6 +479,7 @@ void sg14_test::slot_map_test() InsertEraseStressTest([i=7]() mutable { return std::make_unique(++i); }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); } #if defined(__cpp_concepts)