Add new algorithm 'gather'

[SVN r82584]
This commit is contained in:
Marshall Clow
2013-01-22 00:44:53 +00:00
parent 771375973f
commit 4ca6b10441
7 changed files with 681 additions and 1 deletions

View File

@ -58,6 +58,7 @@ Thanks to all the people who have reviewed this library and made suggestions for
[section:Misc Other Algorithms] [section:Misc Other Algorithms]
[include clamp-hpp.qbk] [include clamp-hpp.qbk]
[include gather.qbk]
[include hex.qbk] [include hex.qbk]
[endsect] [endsect]

79
doc/gather.qbk Normal file
View File

@ -0,0 +1,79 @@
[/ File gather.qbk]
[section:gather gather]
[/license
Copyright (c) 2013 Marshall Clow
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
The header file 'boost/algorithm/gather.hpp' contains two variants of a single algorithm, `gather`.
`gather()` takes a collection of elements defined by a pair of iterators and moves the ones satisfying a predicate to them to a position (called the pivot) within the sequence. The algorithm is stable. The result is a pair of iterators that contains the items that satisfy the predicate.
[heading Interface]
The function `gather` returns a `std::pair` of iterators that denote the elements that satisfy the predicate.
There are two versions; one takes two iterators, and the other takes a range.
``
namespace boost { namespace algorithm {
template <typename ForwardIterator, typename Pred>
std::pair<ForwardIterator,ForwardIterator>
gather ( ForwardIterator first, ForwardIterator last, ForwardIterator pivot, Pred pred );
template <typename ForwardRange, typename Pred>
std::pair<typename boost::range_iterator<ForwardRange>::type, typename boost::range_iterator<ForwardRange>::type>
gather ( ForwardRange &range, typename boost::range_iterator<ForwardRange>::type pivot, Pred pred );
}}
``
[heading Examples]
Given an sequence containing:
``
0 1 2 3 4 5 6 7 8 9
``
a call to gather ( arr, arr + 10, arr + 4, IsEven ) will result in:
``
1 3 0 2 4 6 8 5 7 9
|---|-----|
first | second
pivot
``
where `first` and `second` are the fields of the pair that is returned by the call.
[heading Iterator Requirements]
`gather` work on all iterators except input or output iterators.
[heading Storage Requirements]
`gather` uses stable_partition, which will attempt to allocate temporary memory, but will work in-situ if there is none available.
[heading Complexity]
If there is sufficient memory available, the run time is linear: `O(N)`
If there is not any memory available, then the run time is `O(N log N)`.
[heading Exception Safety]
[heading Notes]
[endsect]
[/ File gather.qbk
Copyright 2013 Marshall Clow
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt).
]

View File

@ -0,0 +1,122 @@
/*
Copyright 2008 Adobe Systems Incorporated
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Revision history:
January 2008 mtc Version for Adobe Source Library
January 2013 mtc Version for Boost.Algorithm
*/
/**************************************************************************************************/
/*!
\author Marshall Clow
\date January 2008
*/
#ifndef BOOST_ALGORITHM_GATHER_HPP
#define ADOBE_ALGORITHM_GATHER_HPP
#include <algorithm> // for std::stable_partition
#include <functional>
#include <boost/bind.hpp> // for boost::bind
#include <boost/range/begin.hpp> // for boost::begin(range)
#include <boost/range/end.hpp> // for boost::end(range)
/**************************************************************************************************/
/*!
\defgroup gather gather
\ingroup mutating_algorithm
\c gather() takes a collection of elements defined by a pair of iterators and moves
the ones satisfying a predicate to them to a position (called the pivot) within
the sequence. The algorithm is stable. The result is a pair of iterators that
contains the items that satisfy the predicate.
Given an sequence containing:
<pre>
0 1 2 3 4 5 6 7 8 9
</pre>
a call to gather ( arr, arr + 10, arr + 4, IsEven ()) will result in:
<pre>
1 3 0 2 4 6 8 5 7 9
|---|-----|
first | second
pivot
</pre>
The problem is broken down into two basic steps, namely, moving the items before the pivot
and then moving the items from the pivot to the end. These "moves" are done with calls to
stable_partition.
\par Storage Requirements:
The algorithm uses stable_partition, which will attempt to allocate temporary memory,
but will work in-situ if there is none available.
\par Time Complexity:
If there is sufficient memory available, the run time is linear in <code>N</code>.
If there is not any memory available, then the run time is <code>O(N log N)</code>.
*/
/**************************************************************************************************/
namespace boost { namespace algorithm {
/**************************************************************************************************/
/*!
\ingroup gather
\brief iterator-based gather implementation
*/
template <
typename ForwardIterator, // Iter models ForwardIterator
typename Pred> // Pred models UnaryPredicate
std::pair<ForwardIterator,ForwardIterator> gather ( ForwardIterator first, ForwardIterator last, ForwardIterator pivot, Pred pred )
{
// The first call partitions everything up to (but not including) the pivot element,
// while the second call partitions the rest of the sequence.
return std::make_pair (
std::stable_partition ( first, pivot, !boost::bind<bool> ( pred, _1 )),
std::stable_partition ( pivot, last, boost::bind<bool> ( pred, _1 )));
}
/**************************************************************************************************/
/*!
\ingroup gather
\brief range-based gather implementation
*/
template <
typename ForwardRange, //
typename Pred> // Pred models UnaryPredicate
std::pair<
typename boost::range_iterator<ForwardRange>::type,
typename boost::range_iterator<ForwardRange>::type>
gather (
ForwardRange &range,
typename boost::range_iterator<ForwardRange>::type pivot,
Pred pred )
{
return boost::algorithm::gather ( boost::begin ( range ), boost::end ( range ), pivot, pred );
}
/**************************************************************************************************/
}} // namespace
/**************************************************************************************************/
#endif

View File

@ -46,10 +46,14 @@ import testing ;
[ run hex_test2.cpp : : : : hex_test2 ] [ run hex_test2.cpp : : : : hex_test2 ]
[ run hex_test3.cpp : : : : hex_test3 ] [ run hex_test3.cpp : : : : hex_test3 ]
[ run hex_test4.cpp : : : : hex_test4 ] [ run hex_test4.cpp : : : : hex_test4 ]
[ compile-fail hex_fail1.cpp ] [ compile-fail hex_fail1.cpp ]
# Wrapper tests # Wrapper tests
[ run wrapper_test1.cpp : : : : wrapper_test1 ] [ run wrapper_test1.cpp : : : : wrapper_test1 ]
# Gather tests
[ run gather_test1.cpp : : : : gather_test1 ]
[ compile-fail gather_fail1.cpp ]
; ;
} }

39
test/gather_fail1.cpp Normal file
View File

@ -0,0 +1,39 @@
/*
Copyright (c) Marshall Clow 2011-2012.
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
For more information, see http://www.boost.org
*/
#include <iostream>
#include <boost/config.hpp>
#include <boost/algorithm/gather.hpp>
#include <boost/test/included/test_exec_monitor.hpp>
#include <string>
#include <vector>
#include <list>
#include "iterator_test.hpp"
namespace ba = boost::algorithm;
bool is_ten ( int i ) { return i == 10; }
void test_sequence1 () {
std::vector<int> v;
typedef input_iterator<std::vector<int>::iterator> II;
// This should fail to compile, since gather doesn't work with input iterators
(void) ba::gather ( II( v.begin ()), II( v.end ()), II( v.begin ()), is_even );
}
int test_main( int , char* [] )
{
test_sequence1 ();
return 0;
}

139
test/gather_test1.cpp Normal file
View File

@ -0,0 +1,139 @@
/*
Copyright (c) Marshall Clow 2011-2012.
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
For more information, see http://www.boost.org
*/
#include <iostream>
#include <boost/config.hpp>
#include <boost/algorithm/gather.hpp>
#include <boost/test/included/test_exec_monitor.hpp>
#include <string>
#include <vector>
#include <list>
#include "iterator_test.hpp"
namespace ba = boost::algorithm;
template <typename Container>
void print ( const char *prompt, const Container &c ) {
std::cout << prompt << " { ";
std::copy ( c.begin (), c.end (), std::ostream_iterator<typename Container::value_type>(std::cout, " "));
std::cout << std::endl;
}
template <typename Iterator, typename Predicate>
void test_iterators ( Iterator first, Iterator last, Predicate comp, std::size_t offset ) {
// Create the pivot point
Iterator off = first;
std::advance(off, offset);
// Gather the elements
std::pair<Iterator, Iterator> res = ba::gather ( first, last, off, comp );
// We should now have three sequences, any of which may be empty:
// * [begin .. result.first) - items that do not satisfy the predicate
// * [result.first .. result.second) - items that do satisfy the predicate
// * [result.second .. end) - items that do not satisfy the predicate
Iterator iter = first;
for ( ; iter != res.first; ++iter )
BOOST_CHECK ( !comp ( *iter ));
for ( ; iter != res.second; ++iter)
BOOST_CHECK ( comp ( *iter ));
for ( ; iter != last; ++iter )
BOOST_CHECK ( !comp ( *iter ));
}
template <typename Container, typename Predicate>
void test_iterator_types ( const Container &c, Predicate comp, std::size_t offset ) {
typedef std::vector<typename Container::value_type> vec;
typedef forward_iterator<typename vec::iterator> FI;
typedef bidirectional_iterator<typename vec::iterator> BDI;
typedef random_access_iterator<typename vec::iterator> RAI;
vec v;
v.assign ( c.begin (), c.end ());
test_iterators ( FI ( v.begin ()), FI ( v.end ()), comp, offset );
v.assign ( c.begin (), c.end ());
test_iterators ( BDI ( v.begin ()), BDI ( v.end ()), comp, offset );
v.assign ( c.begin (), c.end ());
test_iterators ( RAI ( v.begin ()), RAI ( v.end ()), comp, offset );
}
template <typename T>
struct less_than {
public:
// typedef T argument_type;
// typedef bool result_type;
less_than ( T foo ) : val ( foo ) {}
less_than ( const less_than &rhs ) : val ( rhs.val ) {}
bool operator () ( const T &v ) const { return v < val; }
private:
less_than ();
less_than operator = ( const less_than &rhs );
T val;
};
bool is_even ( int i ) { return i % 2 == 0; }
bool is_ten ( int i ) { return i == 10; }
void test_sequence1 () {
std::vector<int> v;
for ( int i = 5; i < 15; ++i )
v.push_back ( i );
test_iterator_types ( v, less_than<int>(10), 0 ); // at beginning
test_iterator_types ( v, less_than<int>(10), 5 );
test_iterator_types ( v, less_than<int>(10), v.size () - 1 ); // at end
test_iterator_types ( v, is_even, 0 );
test_iterator_types ( v, is_even, 5 );
test_iterator_types ( v, is_even, v.size () - 1 );
// Exactly one element in the sequence matches
test_iterator_types ( v, is_ten, 0 );
test_iterator_types ( v, is_ten, 5 );
test_iterator_types ( v, is_ten, v.size () - 1 );
// Everything in the sequence matches
test_iterator_types ( v, less_than<int>(99), 0 );
test_iterator_types ( v, less_than<int>(99), 5 );
test_iterator_types ( v, less_than<int>(99), v.size () - 1 );
// Nothing in the sequence matches
test_iterator_types ( v, less_than<int>(0), 0 );
test_iterator_types ( v, less_than<int>(0), 5 );
test_iterator_types ( v, less_than<int>(0), v.size () - 1 );
// All the elements in the sequence are the same
v.clear ();
for ( int i = 0; i < 11; ++i )
v.push_back ( 10 );
// Everything in the sequence matches
test_iterator_types ( v, is_ten, 0 );
test_iterator_types ( v, is_ten, 5 );
test_iterator_types ( v, is_ten, v.size () - 1 );
// Nothing in the sequence matches
test_iterator_types ( v, less_than<int>(5), 0 );
test_iterator_types ( v, less_than<int>(5), 5 );
test_iterator_types ( v, less_than<int>(5), v.size () - 1 );
}
int test_main( int , char* [] )
{
test_sequence1 ();
return 0;
}

296
test/iterator_test.hpp Normal file
View File

@ -0,0 +1,296 @@
#ifndef ITERATOR_TEST_H
#define ITERATOR_TEST_H
/*
A set of iterator adapters for constructing test cases
From an iterator (or a pointer), you can make any class of iterator.
Assuming you want to degrade the capabilities.
Modeled closely on work that Howard Hinnant did for libc++.
*/
#include <iterator>
// == Input Iterator ==
template <typename It>
class input_iterator {
public:
typedef std::input_iterator_tag iterator_category;
typedef typename std::iterator_traits<It>::value_type value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
It base() const {return it_;}
input_iterator() : it_() {}
explicit input_iterator(It it) : it_(it) {}
template <typename U>
input_iterator(const input_iterator<U>& u) :it_(u.it_) {}
reference operator*() const {return *it_;}
pointer operator->() const {return it_;}
input_iterator& operator++() {++it_; return *this;}
input_iterator operator++(int) {input_iterator tmp(*this); ++(*this); return tmp;}
friend bool operator==(const input_iterator& x, const input_iterator& y)
{return x.it_ == y.it_;}
friend bool operator!=(const input_iterator& x, const input_iterator& y)
{return !(x == y);}
private:
It it_;
template <typename U> friend class input_iterator;
};
template <typename T, typename U>
inline bool
operator==(const input_iterator<T>& x, const input_iterator<U>& y)
{
return x.base() == y.base();
}
template <typename T, typename U>
inline bool
operator!=(const input_iterator<T>& x, const input_iterator<U>& y)
{
return !(x == y);
}
// == Forward Iterator ==
template <typename It>
class forward_iterator {
public:
typedef std::forward_iterator_tag iterator_category;
typedef typename std::iterator_traits<It>::value_type value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
It base() const {return it_;}
forward_iterator() : it_() {}
explicit forward_iterator(It it) : it_(it) {}
template <typename U>
forward_iterator(const forward_iterator<U>& u) :it_(u.it_) {}
reference operator*() const {return *it_;}
pointer operator->() const {return it_;}
forward_iterator& operator++() {++it_; return *this;}
forward_iterator operator++(int) {forward_iterator tmp(*this); ++(*this); return tmp;}
friend bool operator==(const forward_iterator& x, const forward_iterator& y)
{return x.it_ == y.it_;}
friend bool operator!=(const forward_iterator& x, const forward_iterator& y)
{return !(x == y);}
private:
It it_;
template <typename U> friend class forward_iterator;
};
template <typename T, typename U>
inline bool
operator==(const forward_iterator<T>& x, const forward_iterator<U>& y)
{
return x.base() == y.base();
}
template <typename T, typename U>
inline bool
operator!=(const forward_iterator<T>& x, const forward_iterator<U>& y)
{
return !(x == y);
}
// == Bidirectional Iterator ==
template <typename It>
class bidirectional_iterator
{
public:
typedef std::bidirectional_iterator_tag iterator_category;
typedef typename std::iterator_traits<It>::value_type value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
It base() const {return it_;}
bidirectional_iterator() : it_() {}
explicit bidirectional_iterator(It it) : it_(it) {}
template <typename U>
bidirectional_iterator(const bidirectional_iterator<U>& u) :it_(u.it_) {}
reference operator*() const {return *it_;}
pointer operator->() const {return it_;}
bidirectional_iterator& operator++() {++it_; return *this;}
bidirectional_iterator operator++(int) {bidirectional_iterator tmp(*this); ++(*this); return tmp;}
bidirectional_iterator& operator--() {--it_; return *this;}
bidirectional_iterator operator--(int) {bidirectional_iterator tmp(*this); --(*this); return tmp;}
private:
It it_;
template <typename U> friend class bidirectional_iterator;
};
template <typename T, typename U>
inline bool
operator==(const bidirectional_iterator<T>& x, const bidirectional_iterator<U>& y)
{
return x.base() == y.base();
}
template <typename T, typename U>
inline bool
operator!=(const bidirectional_iterator<T>& x, const bidirectional_iterator<U>& y)
{
return !(x == y);
}
// == Random Access Iterator ==
template <typename It>
class random_access_iterator {
public:
typedef std::random_access_iterator_tag iterator_category;
typedef typename std::iterator_traits<It>::value_type value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
It base() const {return it_;}
random_access_iterator() : it_() {}
explicit random_access_iterator(It it) : it_(it) {}
template <typename U>
random_access_iterator(const random_access_iterator<U>& u) :it_(u.it_) {}
reference operator*() const {return *it_;}
pointer operator->() const {return it_;}
random_access_iterator& operator++() {++it_; return *this;}
random_access_iterator operator++(int) {random_access_iterator tmp(*this); ++(*this); return tmp;}
random_access_iterator& operator--() {--it_; return *this;}
random_access_iterator operator--(int) {random_access_iterator tmp(*this); --(*this); return tmp;}
random_access_iterator& operator+=(difference_type n) {it_ += n; return *this;}
random_access_iterator operator+ (difference_type n) const {random_access_iterator tmp(*this); tmp += n; return tmp;}
friend random_access_iterator operator+(difference_type n, random_access_iterator x) {x += n; return x;}
random_access_iterator& operator-=(difference_type n) {return *this += -n;}
random_access_iterator operator- (difference_type n) const {random_access_iterator tmp(*this); tmp -= n; return tmp;}
reference operator[](difference_type n) const {return it_[n];}
private:
It it_;
template <typename U> friend class random_access_iterator;
};
template <typename T, typename U>
inline bool
operator==(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return x.base() == y.base();
}
template <typename T, typename U>
inline bool
operator!=(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return !(x == y);
}
template <typename T, typename U>
inline bool
operator<(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return x.base() < y.base();
}
template <typename T, typename U>
inline bool
operator<=(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return !(y < x);
}
template <typename T, typename U>
inline bool
operator>(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return y < x;
}
template <typename T, typename U>
inline bool
operator>=(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return !(x < y);
}
template <typename T, typename U>
inline typename std::iterator_traits<T>::difference_type
operator-(const random_access_iterator<T>& x, const random_access_iterator<U>& y)
{
return x.base() - y.base();
}
// == Output Iterator ==
template <typename It>
class output_iterator {
public:
typedef std::output_iterator_tag iterator_category;
typedef void value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
It base() const {return it_;}
output_iterator () {}
explicit output_iterator(It it) : it_(it) {}
template <typename U>
output_iterator(const output_iterator<U>& u) :it_(u.it_) {}
reference operator*() const {return *it_;}
output_iterator& operator++() {++it_; return *this;}
output_iterator operator++(int) {output_iterator tmp(*this); ++(*this); return tmp;}
private:
It it_;
template <typename U> friend class output_iterator;
};
// No comparison operators for output iterators
// == Get the base of an iterator; used for comparisons ==
template <typename Iter>
inline Iter base(output_iterator<Iter> i) { return i.base(); }
template <typename Iter>
inline Iter base(input_iterator<Iter> i) { return i.base(); }
template <typename Iter>
inline Iter base(forward_iterator<Iter> i) { return i.base(); }
template <typename Iter>
inline Iter base(bidirectional_iterator<Iter> i) { return i.base(); }
template <typename Iter>
inline Iter base(random_access_iterator<Iter> i) { return i.base(); }
template <typename Iter> // everything else
inline Iter base(Iter i) { return i; }
#endif // ITERATORS_H