Skip to content

Commit

Permalink
Add a slot_map::partition(Pred) member function.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Quuxplusone committed Sep 24, 2018
1 parent 3e2a576 commit 0ce0fd1
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
34 changes: 34 additions & 0 deletions SG14/slot_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,40 @@ class slot_map
return 1;
}

template<class Pred>
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
Expand Down
40 changes: 40 additions & 0 deletions SG14_test/slot_map_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,39 @@ static void EraseRangeTest()
}
}

template<class SM>
static void PartitionTest()
{
using T = typename SM::mapped_type;
SM sm;
auto key3 = sm.insert(Monad<T>::from_value(3));
auto key1 = sm.insert(Monad<T>::from_value(1));
auto key4 = sm.insert(Monad<T>::from_value(4));
auto key5 = sm.insert(Monad<T>::from_value(5));
auto key9 = sm.insert(Monad<T>::from_value(9));
auto key2 = sm.insert(Monad<T>::from_value(2));
auto key6 = sm.insert(Monad<T>::from_value(6));

auto pivot = sm.partition([](const auto& elt) {
return Monad<T>::value_of(elt) >= 5;
});

for (auto it = sm.begin(); it != pivot; ++it) {
assert(Monad<T>::value_of(*it) >= 5);
}
for (auto it = pivot; it != sm.end(); ++it) {
assert(Monad<T>::value_of(*it) < 5);
}

assert(Monad<T>::value_of(*sm.find(key3)) == 3);
assert(Monad<T>::value_of(*sm.find(key1)) == 1);
assert(Monad<T>::value_of(*sm.find(key4)) == 4);
assert(Monad<T>::value_of(*sm.find(key5)) == 5);
assert(Monad<T>::value_of(*sm.find(key9)) == 9);
assert(Monad<T>::value_of(*sm.find(key2)) == 2);
assert(Monad<T>::value_of(*sm.find(key6)) == 6);
}

static void TypedefTests()
{
if (true) {
Expand Down Expand Up @@ -385,6 +418,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_1>([i=3]() mutable { return ++i; });
EraseInLoopTest<slot_map_1>();
EraseRangeTest<slot_map_1>();
PartitionTest<slot_map_1>();

// Test slot_map with a custom key type (C++14 destructuring).
using slot_map_2 = stdext::slot_map<unsigned long, TestKey::key_16_8_t>;
Expand All @@ -393,6 +427,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_2>([i=5]() mutable { return ++i; });
EraseInLoopTest<slot_map_2>();
EraseRangeTest<slot_map_2>();
PartitionTest<slot_map_2>();

#if __cplusplus >= 201703L
// Test slot_map with a custom key type (C++17 destructuring).
Expand All @@ -402,6 +437,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_3>([i=3]() mutable { return ++i; });
EraseInLoopTest<slot_map_3>();
EraseRangeTest<slot_map_3>();
PartitionTest<slot_map_3>();
#endif // __cplusplus >= 201703L

// Test slot_map with a custom (but standard and random-access) container type.
Expand All @@ -411,6 +447,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_4>([i=7]() mutable { return ++i; });
EraseInLoopTest<slot_map_4>();
EraseRangeTest<slot_map_4>();
PartitionTest<slot_map_4>();

// Test slot_map with a custom (non-standard, random-access) container type.
using slot_map_5 = stdext::slot_map<int, std::pair<unsigned, unsigned>, TestContainer::Vector>;
Expand All @@ -419,6 +456,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_5>([i=7]() mutable { return ++i; });
EraseInLoopTest<slot_map_5>();
EraseRangeTest<slot_map_5>();
PartitionTest<slot_map_5>();

// Test slot_map with a custom (standard, bidirectional-access) container type.
using slot_map_6 = stdext::slot_map<int, std::pair<unsigned, unsigned>, std::list>;
Expand All @@ -427,6 +465,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_6>([i=7]() mutable { return ++i; });
EraseInLoopTest<slot_map_6>();
EraseRangeTest<slot_map_6>();
PartitionTest<slot_map_6>();

// 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.
Expand All @@ -440,6 +479,7 @@ void sg14_test::slot_map_test()
InsertEraseStressTest<slot_map_7>([i=7]() mutable { return std::make_unique<int>(++i); });
EraseInLoopTest<slot_map_7>();
EraseRangeTest<slot_map_7>();
PartitionTest<slot_map_7>();
}

#if defined(__cpp_concepts)
Expand Down

0 comments on commit 0ce0fd1

Please sign in to comment.