From 1de89ab51cc3ad97b7e01f5e37aaeffd3f0be13b Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Sun, 23 Sep 2018 18:22:52 -0700 Subject: [PATCH] Add a slot_map::partition(Pred) member function. Just like the standard `std::partition` algorithm, this member function reorders the slot_map's elements in such a way that all elements for which the predicate `pred` returns `true` precede the elements for which `pred` returns `false`. The relative order of the elements is not preserved. The function returns an iterator to the first element of the second group (which may be `end()`, in the case that all elements belong to the first group). Addresses part of #131, although I'm not sure whether this really addresses whatever use case @p-groarke is thinking of. --- include/sg14/slot_map.h | 34 ++++++++++++++++++++++++++++++++++ test/slot_map_test.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/sg14/slot_map.h b/include/sg14/slot_map.h index dc043dc..fc3573f 100644 --- a/include/sg14/slot_map.h +++ b/include/sg14/slot_map.h @@ -302,6 +302,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/test/slot_map_test.cpp b/test/slot_map_test.cpp index d1fcc24..e27bf20 100644 --- a/test/slot_map_test.cpp +++ b/test/slot_map_test.cpp @@ -290,6 +290,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); +} + template static void ReserveTest() { @@ -523,6 +556,7 @@ TEST(slot_map, Basic) InsertEraseStressTest([i=3]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(true); GenerationsDontSkipTest(); @@ -539,6 +573,7 @@ TEST(slot_map, CustomKeyType) InsertEraseStressTest([i=5]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(true); GenerationsDontSkipTest(); @@ -553,6 +588,7 @@ TEST(slot_map, CustomKeyType) InsertEraseStressTest([i=3]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(true); GenerationsDontSkipTest(); @@ -570,6 +606,7 @@ TEST(slot_map, DequeContainer) InsertEraseStressTest([i=7]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(false); GenerationsDontSkipTest(); @@ -587,6 +624,7 @@ TEST(slot_map, CustomRAContainer) InsertEraseStressTest([i=7]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(false); GenerationsDontSkipTest(); @@ -605,6 +643,7 @@ TEST(slot_map, CustomBidiContainer) InsertEraseStressTest([i=7]() mutable { return ++i; }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(false); GenerationsDontSkipTest(); @@ -626,6 +665,7 @@ TEST(slot_map, MoveOnlyValueType) InsertEraseStressTest([i=7]() mutable { return std::make_unique(++i); }); EraseInLoopTest(); EraseRangeTest(); + PartitionTest(); ReserveTest(); VerifyCapacityExists(false); GenerationsDontSkipTest();