diff --git a/include/boost/container/experimental/segmented_partition.hpp b/include/boost/container/experimental/segmented_partition.hpp index f20351a..0955025 100644 --- a/include/boost/container/experimental/segmented_partition.hpp +++ b/include/boost/container/experimental/segmented_partition.hpp @@ -32,8 +32,12 @@ FwdIt segmented_partition(FwdIt first, Sent last, Pred pred); namespace detail_algo { -template -OutIter partition_scan(FwdIt first, FwdIt last, OutIter result, Pred pred, non_segmented_iterator_tag) +////////////////////////////////////////////// +// Forward (Lomuto-style) partition +////////////////////////////////////////////// + +template +OutIter partition_scan(FwdIt first, Sent last, OutIter result, Pred pred, non_segmented_iterator_tag, const std::forward_iterator_tag &) { for(; first != last; ++first) { if(pred(*first)) { @@ -45,7 +49,7 @@ OutIter partition_scan(FwdIt first, FwdIt last, OutIter result, Pred pred, non_s } template -OutIter partition_scan(SegIt first, SegIt last, OutIter result, Pred pred, segmented_iterator_tag) +OutIter partition_scan(SegIt first, SegIt last, OutIter result, Pred pred, segmented_iterator_tag, const std::forward_iterator_tag &) { typedef segmented_iterator_traits traits; typedef typename traits::local_iterator local_iterator; @@ -69,40 +73,140 @@ OutIter partition_scan(SegIt first, SegIt last, OutIter result, Pred pred, segme } } -template -BOOST_CONTAINER_FORCEINLINE SegIter segmented_partition_dispatch - (SegIter first, SegIter last, Pred pred, segmented_iterator_tag) +////////////////////////////////////////////// +// Bidirectional (Hoare-style) partition +////////////////////////////////////////////// + +template +BidirIt partition_scan(BidirIt first, BidirIt last, Pred pred, non_segmented_iterator_tag, const std::bidirectional_iterator_tag&) { - return partition_scan(first, last, first, pred, segmented_iterator_tag()); + while(true) { + while(first != last && pred(*first)) + ++first; + if(first == last) + return first; + --last; + while(first != last && !pred(*last)) + --last; + if(first == last) + return first; + boost::adl_move_swap(*first, *last); + ++first; + } +} + +template +SegIt partition_scan(SegIt first, SegIt last, Pred pred, segmented_iterator_tag, const std::bidirectional_iterator_tag& cat) +{ + typedef segmented_iterator_traits traits; + typedef typename traits::local_iterator local_iterator; + typedef typename traits::segment_iterator segment_iterator; + typedef typename segmented_iterator_traits::is_segmented_iterator is_local_seg_t; + + if(first == last) return first; + + segment_iterator sf = traits::segment(first); + segment_iterator sl = traits::segment(last); + + if(sf == sl) { + local_iterator r = partition_scan(traits::local(first), traits::local(last), pred, is_local_seg_t(), cat); + return traits::compose(sf, r); + } + + local_iterator f_loc = traits::local(first); + local_iterator f_end = traits::end(sf); + local_iterator l_loc = traits::local(last); + local_iterator l_beg = traits::begin(sl); + + for(;;) { + // Phase 1: advance front to find element NOT satisfying pred + while(f_loc != f_end && pred(*f_loc)) + ++f_loc; + + if(f_loc == f_end) { + ++sf; + if(sf == sl) { + local_iterator r = partition_scan(l_beg, l_loc, pred, is_local_seg_t(), cat); + return traits::compose(sf, r); + } + f_loc = traits::begin(sf); + f_end = traits::end(sf); + continue; + } + + // Phase 2: retreat back to find element satisfying pred + for(;;) { + if(l_loc == l_beg) { + --sl; + if(sf == sl) { + local_iterator r = partition_scan(f_loc, f_end, pred, is_local_seg_t(), cat); + return traits::compose(sf, r); + } + l_beg = traits::begin(sl); + l_loc = traits::end(sl); + } + --l_loc; + if(pred(*l_loc)) break; + } + + boost::adl_move_swap(*f_loc, *l_loc); + ++f_loc; + } +} + +////////////////////////////////////////////// +// Top-level dispatch +////////////////////////////////////////////// + +template +BOOST_CONTAINER_FORCEINLINE +FwdIt segmented_partition_dispatch(FwdIt first, Sent last, Pred pred, Tag tag, const std::bidirectional_iterator_tag& cat) +{ + return (partition_scan)(first, last, pred, tag, cat); } template -typename algo_enable_if_c< - !Tag::value || is_sentinel::value, FwdIt>::type -segmented_partition_dispatch(FwdIt first, Sent last, Pred pred, Tag) +BOOST_CONTAINER_FORCEINLINE +FwdIt segmented_partition_dispatch(FwdIt first, Sent last, Pred pred, Tag tag, const std::forward_iterator_tag &cat) { - FwdIt result = first; - for(; first != last; ++first) { - if(pred(*first)) { - boost::adl_move_swap(*result, *first); - ++result; - } - } - return result; + return (partition_scan)(first, last, first, pred, tag, cat); } +template +struct sent_filter +{ + typedef std::forward_iterator_tag cat_t; + typedef non_segmented_iterator_tag seg_t; +}; + +template +struct sent_filter +{ + typedef Tag cat_t; + typedef Seg seg_t; +}; + } // namespace detail_algo //! Reorders elements in [first, last) so that elements satisfying -//! \c pred come before those that do not (Lomuto-style partition). +//! \c pred come before those that do not. +//! For forward iterators, uses a Lomuto-style scan. +//! For bidirectional (or stronger) iterators, uses a Hoare-style +//! partition that swaps from both ends, reducing the number of swaps. //! Returns an iterator to the partition point. template BOOST_CONTAINER_FORCEINLINE FwdIt segmented_partition(FwdIt first, Sent last, Pred pred) { typedef segmented_iterator_traits traits; + typedef typename boost::container::iterator_traits::iterator_category cat_t; + typedef typename traits::is_segmented_iterator seg_t; + + typedef detail_algo::sent_filter sent_filter_t; return detail_algo::segmented_partition_dispatch - (first, last, pred, typename traits::is_segmented_iterator()); + ( first, last, pred + , typename sent_filter_t::seg_t() + , typename sent_filter_t::cat_t()); } } // namespace container