Skip to content
Snippets Groups Projects
Commit 3b6127c4 authored by Rosen Matev's avatar Rosen Matev :sunny:
Browse files

Merge branch 'add-PartitionPosition' into 'master'

Add {stableP,p}artition{Position,ByMask}, and bind_front

See merge request !2312
parents e577f4cf 75ae6188
No related branches found
No related tags found
1 merge request!2312Add {stableP,p}artition{Position,ByMask}, and bind_front
Pipeline #1371131 passed
......@@ -48,6 +48,8 @@ gaudi_install_python_modules()
gaudi_add_test(QMTest QMTEST)
gaudi_add_unit_test(test_PartitionPosition tests/src/test_PartitionPosition.cpp
LINK_LIBRARIES LHCbKernel TYPE Boost)
gaudi_add_unit_test(test_arenaAllocator tests/src/test_arenaAllocator.cpp
LINK_LIBRARIES LHCbKernel TYPE Boost)
gaudi_add_unit_test(test_container tests/src/test_container.cpp
......
/*****************************************************************************\
* (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration *
* *
* This software is distributed under the terms of the GNU General Public *
* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". *
* *
* In applying this licence, CERN does not waive the privileges and immunities *
* granted to it by virtue of its status as an Intergovernmental Organization *
* or submit itself to any jurisdiction. *
\*****************************************************************************/
#include <algorithm>
#include <iterator>
namespace LHCb::cxx {
namespace details {
// this version works for forward iterators, loops simpler, but swaps up to N-1 times
template <typename Iterator, typename Predicate>
Iterator partitionPosition( Iterator first, Iterator last, Predicate pred, std::forward_iterator_tag ) {
for ( ; first != last && pred( first ); ++first )
;
if ( first == last ) return first;
for ( auto i = std::next( first ); i != last; ++i ) {
if ( pred( i ) ) {
std::iter_swap( i, first ); // the 'bulldozer` approach...
++first;
}
}
return first;
}
// this version requires bidirectional iterators, and does the minimal number of swaps
// (namely N/2) at the cost of more complex looping / memory access patterns...
template <typename Iterator, typename Predicate>
Iterator partitionPosition( Iterator first, Iterator last, Predicate pred, std::bidirectional_iterator_tag ) {
for ( ; first != last && pred( first ); ++first )
; /* empty on purpose */
for ( ; std::distance( first, last ) > 1 && !pred( std::prev( last ) ); --last )
; /* empty on purpose */
if ( std::distance( first, last ) < 2 ) return first;
std::iter_swap( first++, --last );
return partitionPosition( first, last, pred, std::bidirectional_iterator_tag{} );
}
} // namespace details
// Reorders the elements in the range [first, last) in such a way that
// all elements for which the predicate p returns true precede the
// elements for which predicate p returns false.
// Relative order of the elements is preserved
//
// The difference with `std::stable_partition` is that here, the
// predicate is given the _position_ of the item as argument, and
// not the item itself. This allows to partition based on eg. an
// external bitset
//
// See the unit test for an example...
//
// adapted from https://sean-parent.stlab.cc/presentations/2015-09-23-data-structures/data-structures.pdf,
// page 31
//
// TODO??: add constraint that pred(first) must return a bool?
//
template <typename Iterator, typename Predicate>
Iterator stablePartitionPosition( Iterator first, Iterator last, Predicate pred ) {
auto n = std::distance( first, last );
if ( n == 0 ) return first;
if ( n == 1 ) return first + pred( first );
auto middle = std::next( first, n / 2 );
return std::rotate( stablePartitionPosition( first, middle, pred ), middle,
stablePartitionPosition( middle, last, pred ) );
}
// Reorders the elements in the range [first, last) in such a way that
// all elements for which the predicate p returns true precede the
// elements for which predicate p returns false.
// Relative order of the elements is _not_ preserved
//
// The difference with `std::partition` is that here, the
// predicate is given the _position_ of the item as argument, and
// not the item itself. This allows to partition based on eg. an
// external bitset
template <typename Iterator, typename Predicate>
Iterator partitionPosition( Iterator first, Iterator last, Predicate pred ) {
return details::partitionPosition( first, last, pred,
typename std::iterator_traits<Iterator>::iterator_category{} );
}
// Reorders the elements in `range` in such a way that
// all elements selected by `mask` precede the
// elements not selected by `mask`.
// Relative order of the elements is preserved
//
template <typename Range, typename Mask>
auto stablePartitionByMask( Range range, Mask mask ) {
using std::begin;
using std::end;
auto first = begin( range );
auto pivot =
stablePartitionPosition( first, end( range ), [&]( auto i ) { return mask[std::distance( first, i )]; } );
return std::distance( first, pivot );
}
// Reorders the elements in `range` in such a way that
// all elements selected by `mask` precede the
// elements not selected by `mask`.
// Relative order of the elements is _not_ preserved
//
template <typename Range, typename Mask>
auto partitionByMask( Range range, Mask mask ) {
using std::begin;
using std::end;
auto first = begin( range );
auto pivot = partitionPosition( first, end( range ), [&]( auto i ) { return mask[std::distance( first, i )]; } );
return std::distance( first, pivot );
}
} // namespace LHCb::cxx
......@@ -17,6 +17,7 @@
#include <array>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
......@@ -46,6 +47,20 @@
namespace LHCb {
namespace cxx {
// FIXME: C++20: replace with std::bind_front
template <typename F, typename... BoundArgs>
auto bind_front( F&& f, BoundArgs&&... boundArgs ) {
return [f = std::forward<F>( f ),
boundArgs = std::tuple{std::forward<BoundArgs>( boundArgs )...}]( auto&&... args ) -> decltype( auto ) {
return std::apply(
f, std::tuple_cat( boundArgs, std::forward_as_tuple( std::forward<decltype( args )>( args )... ) ) );
};
}
} // namespace cxx
// TODO: when we use a more recent version of range-v3, switch to its version of span
using gsl::span;
......
/*****************************************************************************\
* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration *
* *
* This software is distributed under the terms of the GNU General Public *
* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". *
* *
* In applying this licence, CERN does not waive the privileges and immunities *
* granted to it by virtue of its status as an Intergovernmental Organization *
* or submit itself to any jurisdiction. *
\*****************************************************************************/
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE test_PlatformInfo
#include <boost/test/unit_test.hpp>
#include "Kernel/PartitionPosition.h"
#include "Kernel/STLExtensions.h"
#include <array>
#include <bitset>
BOOST_AUTO_TEST_CASE( masked_stable_partition_array ) {
int a[] = {1, 2, 3, 4, 5, 5, 4, 3, 2, 1};
bool b[] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0};
auto s = LHCb::span( a );
auto n = LHCb::cxx::stablePartitionByMask( s, b );
BOOST_CHECK( n == 4 );
auto expected_first = {2, 4, 4, 2};
auto expected_second = {1, 3, 5, 5, 3, 1};
auto expected_third = {2, 4, 4, 2, 1, 3, 5, 5, 3, 1};
BOOST_TEST( s.first( n ) == expected_first, boost::test_tools::per_element() );
BOOST_TEST( s.subspan( n ) == expected_second, boost::test_tools::per_element() );
BOOST_TEST( a == expected_third, boost::test_tools::per_element() );
}
BOOST_AUTO_TEST_CASE( masked_stable_partition_bitset ) {
auto a = std::array{1, 2, 3, 4, 5, 5, 4, 3, 2, 1};
std::bitset<10> b{0b0101001010};
auto s = LHCb::make_span( a );
auto n = LHCb::cxx::stablePartitionByMask( s, b );
BOOST_CHECK( n == 4 );
auto expected_first = {2, 4, 4, 2};
auto expected_second = {1, 3, 5, 5, 3, 1};
auto expected_third = {2, 4, 4, 2, 1, 3, 5, 5, 3, 1};
BOOST_TEST( s.first( n ) == expected_first, boost::test_tools::per_element() );
BOOST_TEST( s.subspan( n ) == expected_second, boost::test_tools::per_element() );
BOOST_TEST( a == expected_third, boost::test_tools::per_element() );
}
BOOST_AUTO_TEST_CASE( masked_partition_bitset ) {
auto a = std::array{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::bitset<10> b{0b010101010};
auto s = LHCb::make_span( a );
auto n = LHCb::cxx::partitionByMask( s, b );
BOOST_CHECK( n == 4 );
auto expected_first = {8, 2, 6, 4};
auto expected_second = {5, 3, 7, 1, 9};
auto expected_third = {8, 2, 6, 4, 5, 3, 7, 1, 9};
BOOST_TEST( s.first( n ) == expected_first, boost::test_tools::per_element() );
BOOST_TEST( s.subspan( n ) == expected_second, boost::test_tools::per_element() );
BOOST_TEST( a == expected_third, boost::test_tools::per_element() );
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment