diff --git a/include/boost/minmax.hpp b/include/boost/minmax.hpp index 9c4202f..8404828 100644 --- a/include/boost/minmax.hpp +++ b/include/boost/minmax.hpp @@ -1,17 +1,25 @@ // (C) Copyright Eric Niebler 2004. -// Use, modification and distribution are subject to the -// Boost Software License, Version 1.0. (See accompanying file +// (C) Copyright Herve Bronnimann 2004. +// Use, modification and distribution are subject to 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: + 26 June 2004 + Added the code for the boost minmax library. (Herve) 25 February 2004 - Initial version. + Initial version. (Eric) */ #ifndef BOOST_MINMAX_HPP #define BOOST_MINMAX_HPP +/* + * These macros by Eric Niebler should move into a config file. + * I'll leave them for now, until Eric sees fit to remove them. + */ + #include // for std::min and std::max #include @@ -21,4 +29,563 @@ #define BOOST_USING_STD_MAX()\ using std::max + +/* PROPOSED STANDARD EXTENSIONS: + * + * minmax(a, b) + * Effect: (b // for std::pair and std::make_pair +#include // for boost::iterator_value + +namespace boost { + + template + std::pair + minmax(T& a, T& b) { + return (b + std::pair + minmax(T& a, T& b, BinaryPredicate comp) { + return comp(b,a) ? std::make_pair(b,a) : std::make_pair(a,b); + } + + template + std::pair + minmax(const T& a, const T& b) { + return (b + std::pair + minmax(const T& a, const T& b, BinaryPredicate comp) { + return comp(b,a) ? std::make_pair(b,a) : std::make_pair(a,b); + } + + namespace detail { // for compilation with VC++ 6.0 + + template + std::pair + basic_minmax_element(ForwardIter first, ForwardIter last, Compare comp) + { + if (first == last) + return std::make_pair(last,last); + + ForwardIter min_result = first; + ForwardIter max_result = first; + + // if only one element + ForwardIter second = first; ++second; + if (second == last) + return std::make_pair(min_result, max_result); + + // treat first pair separately (only one comparison for first two elements) + ForwardIter potential_min_result = last; + if (comp(first, second)) + max_result = second; + else { + min_result = second; + potential_min_result = first; + } + + // then each element by pairs, with at most 3 comparisons per pair + first = ++second; if (first != last) ++second; + while (second != last) { + if (comp(first, second)) { + if (comp(first, min_result)) { + min_result = first; + potential_min_result = last; + } + if (comp(max_result, second)) + max_result = second; + } else { + if (comp(second, min_result)) { + min_result = second; + potential_min_result = first; + } + if (comp(max_result, first)) + max_result = first; + } + first = ++second; + if (first != last) ++second; + } + + // if odd number of elements, treat last element + if (first != last) { // odd number of elements + if (comp(first, min_result)) + min_result = first, potential_min_result = last; + else if (comp(max_result, first)) + max_result = first; + } + + // resolve min_result being incorrect with one extra comparison + // (in which case potential_min_result is necessarily the correct result) + if (potential_min_result != last + && !comp(min_result, potential_min_result)) + min_result = potential_min_result; + + return std::make_pair(min_result,max_result); + } + + template + struct less_over_iter { + bool operator()(Iterator const& it1, + Iterator const& it2) const { return *it1 < *it2; } + }; + + template + struct binary_pred_over_iter { + explicit binary_pred_over_iter(BinaryPredicate const& p ) : m_p( p ) {} + bool operator()(Iterator const& it1, + Iterator const& it2) const { return m_p(*it1, *it2); } + private: + BinaryPredicate m_p; + }; + + } // namespace detail + + template + std::pair + minmax_element(ForwardIter first, ForwardIter last) + { + return detail::basic_minmax_element(first, last, + detail::less_over_iter() ); + } + + template + std::pair + minmax_element(ForwardIter first, ForwardIter last, BinaryPredicate comp) + { + return detail::basic_minmax_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + +} + +/* PROPOSED BOOST EXTENSIONS + * In the description below, [rfirst,rlast) denotes the reversed range + * of [first,last). Even though the iterator type of first and last may + * be only a Forward Iterator, it is possible to explain the semantics + * by assuming that it is a Bidirectional Iterator. In the sequel, + * reverse(ForwardIterator&) returns the reverse_iterator adaptor. + * This is not how the functions would be implemented! + * + * first_min_element(first, last) + * Effect: std::min_element(first, last); + * + * first_min_element(first, last, comp) + * Effect: std::min_element(first, last, comp); + * + * last_min_element(first, last) + * Effect: reverse( std::min_element(reverse(last), reverse(first)) ); + * + * last_min_element(first, last, comp) + * Effect: reverse( std::min_element(reverse(last), reverse(first), comp) ); + * + * first_max_element(first, last) + * Effect: std::max_element(first, last); + * + * first_max_element(first, last, comp) + * Effect: max_element(first, last); + * + * last_max_element(first, last) + * Effect: reverse( std::max_element(reverse(last), reverse(first)) ); + * + * last_max_element(first, last, comp) + * Effect: reverse( std::max_element(reverse(last), reverse(first), comp) ); + * + * first_min_first_max_element(first, last) + * Effect: std::make_pair( first_min_element(first, last), + * first_max_element(first, last) ); + * + * first_min_first_max_element(first, last, comp) + * Effect: std::make_pair( first_min_element(first, last, comp), + * first_max_element(first, last, comp) ); + * + * first_min_last_max_element(first, last) + * Effect: std::make_pair( first_min_element(first, last), + * last_max_element(first, last) ); + * + * first_min_last_max_element(first, last, comp) + * Effect: std::make_pair( first_min_element(first, last, comp), + * last_max_element(first, last, comp) ); + * + * last_min_first_max_element(first, last) + * Effect: std::make_pair( last_min_element(first, last), + * first_max_element(first, last) ); + * + * last_min_first_max_element(first, last, comp) + * Effect: std::make_pair( last_min_element(first, last, comp), + * first_max_element(first, last, comp) ); + * + * last_min_last_max_element(first, last) + * Effect: std::make_pair( last_min_element(first, last), + * last_max_element(first, last) ); + * + * last_min_last_max_element(first, last, comp) + * Effect: std::make_pair( last_min_element(first, last, comp), + * last_max_element(first, last, comp) ); + */ + +namespace boost { + + namespace detail { + + template + ForwardIter + basic_first_min_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) return last; + ForwardIter min_result = first; + while (++first != last) + if (comp(first, min_result)) + min_result = first; + return min_result; + } + + template + ForwardIter + basic_last_min_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) return last; + ForwardIter min_result = first; + while (++first != last) + if (!comp(min_result, first)) + min_result = first; + return min_result; + } + + template + ForwardIter + basic_first_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) return last; + ForwardIter max_result = first; + while (++first != last) + if (comp(max_result, first)) + max_result = first; + return max_result; + } + + template + ForwardIter + basic_last_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) return last; + ForwardIter max_result = first; + while (++first != last) + if (!comp(first, max_result)) + max_result = first; + return max_result; + } + + } // namespace detail + + template + ForwardIter + first_min_element(ForwardIter first, ForwardIter last) + { + return detail::basic_first_min_element(first, last, + detail::less_over_iter() ); + } + + template + ForwardIter + first_min_element(ForwardIter first, ForwardIter last, BinaryPredicate comp) + { + return detail::basic_first_min_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + + template + ForwardIter + last_min_element(ForwardIter first, ForwardIter last) + { + return detail::basic_last_min_element(first, last, + detail::less_over_iter() ); + } + + template + ForwardIter + last_min_element(ForwardIter first, ForwardIter last, BinaryPredicate comp) + { + return detail::basic_last_min_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + + template + ForwardIter + first_max_element(ForwardIter first, ForwardIter last) + { + return detail::basic_first_max_element(first, last, + detail::less_over_iter() ); + } + + template + ForwardIter + first_max_element(ForwardIter first, ForwardIter last, BinaryPredicate comp) + { + return detail::basic_first_max_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + + template + ForwardIter + last_max_element(ForwardIter first, ForwardIter last) + { + return detail::basic_last_max_element(first, last, + detail::less_over_iter() ); + } + + template + ForwardIter + last_max_element(ForwardIter first, ForwardIter last, BinaryPredicate comp) + { + return detail::basic_last_max_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + + + // Minmax variants -- comments removed + + namespace detail { + + template + std::pair + basic_first_min_last_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) + return std::make_pair(last,last); + + ForwardIter min_result = first; + ForwardIter max_result = first; + + ForwardIter second = ++first; + if (second == last) + return std::make_pair(min_result, max_result); + + if (comp(second, min_result)) + min_result = second; + else + max_result = second; + + first = ++second; if (first != last) ++second; + while (second != last) { + if (!comp(second, first)) { + if (comp(first, min_result)) + min_result = first; + if (!comp(second, max_result)) + max_result = second; + } else { + if (comp(second, min_result)) + min_result = second; + if (!comp(first, max_result)) + max_result = first; + } + first = ++second; if (first != last) ++second; + } + + if (first != last) { + if (comp(first, min_result)) + min_result = first; + else if (!comp(first, max_result)) + max_result = first; + } + + return std::make_pair(min_result, max_result); + } + + template + std::pair + basic_last_min_first_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) return std::make_pair(last,last); + + ForwardIter min_result = first; + ForwardIter max_result = first; + + ForwardIter second = ++first; + if (second == last) + return std::make_pair(min_result, max_result); + + if (comp(max_result, second)) + max_result = second; + else + min_result = second; + + first = ++second; if (first != last) ++second; + while (second != last) { + if (comp(first, second)) { + if (!comp(min_result, first)) + min_result = first; + if (comp(max_result, second)) + max_result = second; + } else { + if (!comp(min_result, second)) + min_result = second; + if (comp(max_result, first)) + max_result = first; + } + first = ++second; if (first != last) ++second; + } + + if (first != last) { + if (!comp(min_result, first)) + min_result = first; + else if (comp(max_result, first)) + max_result = first; + } + + return std::make_pair(min_result, max_result); + } + + template + std::pair + basic_last_min_last_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + if (first == last) return std::make_pair(last,last); + + ForwardIter min_result = first; + ForwardIter max_result = first; + + ForwardIter second = first; ++second; + if (second == last) + return std::make_pair(min_result,max_result); + + ForwardIter potential_max_result = last; + if (comp(first, second)) + max_result = second; + else { + min_result = second; + potential_max_result = second; + } + + first = ++second; if (first != last) ++second; + while (second != last) { + if (comp(first, second)) { + if (!comp(min_result, first)) + min_result = first; + if (!comp(second, max_result)) { + max_result = second; + potential_max_result = last; + } + } else { + if (!comp(min_result, second)) + min_result = second; + if (!comp(first, max_result)) { + max_result = first; + potential_max_result = second; + } + } + first = ++second; + if (first != last) ++second; + } + + if (first != last) { + if (!comp(min_result, first)) + min_result = first; + if (!comp(first, max_result)) { + max_result = first; + potential_max_result = last; + } + } + + if (potential_max_result != last + && !comp(potential_max_result, max_result)) + max_result = potential_max_result; + + return std::make_pair(min_result,max_result); + } + + } // namespace detail + + template + inline std::pair + first_min_first_max_element(ForwardIter first, ForwardIter last) + { + return minmax_element(first, last); + } + + template + inline std::pair + first_min_first_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + return minmax_element(first, last, comp); + } + + template + std::pair + first_min_last_max_element(ForwardIter first, ForwardIter last) + { + return detail::basic_first_min_last_max_element(first, last, + detail::less_over_iter() ); + } + + template + inline std::pair + first_min_last_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + return detail::basic_first_min_last_max_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + + template + std::pair + last_min_first_max_element(ForwardIter first, ForwardIter last) + { + return detail::basic_last_min_first_max_element(first, last, + detail::less_over_iter() ); + } + + template + inline std::pair + last_min_first_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + return detail::basic_last_min_first_max_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + + template + std::pair + last_min_last_max_element(ForwardIter first, ForwardIter last) + { + return detail::basic_last_min_last_max_element(first, last, + detail::less_over_iter() ); + } + + template + inline std::pair + last_min_last_max_element(ForwardIter first, ForwardIter last, + BinaryPredicate comp) + { + return detail::basic_last_min_last_max_element(first, last, + detail::binary_pred_over_iter(comp) ); + } + +} // namespace boost + #endif // BOOST_MINMAX_HPP diff --git a/minmax/doc/minmax_benchs.html b/minmax/doc/minmax_benchs.html new file mode 100644 index 0000000..15fc9e1 --- /dev/null +++ b/minmax/doc/minmax_benchs.html @@ -0,0 +1,524 @@ + + + + + + + + Boost minmax library + + + +
+

+Minmax_element Performance

+ +

+About performance

+Of course, there are many factors that affect the performance of an algorithm. +The number of comparison is only one, but also branch prediction, pipelining, +locality of reference (affects cache efficiency), etc. In practice, +we observe that when the iterator type is a pointer, +boost::minmax_element +is only a tad slower than +std::min_element, and is even faster +than +boost::first_min_last_max_element! This is even more true +for slower iterators (list<>::iterator or +map<>iterator +for instance). The following experiments were conducted on a Pentium III +500 Mhz running Linux and compiled with g++, version 2.95.2, flags -O3. +In the tables, we use different distributions: Identical means that +all the elements are identical, 2-valued means that we replace the +second half of the identical elements by a distinct element, increasing +means that all the elements are distinct and in increasing order, decreasing +is the reverse, and random is produced by random_shuffle. +
+The program that created these tables is included in the distribution, +under minmax_timer.cpp +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
vector<int>::iteratorIdentical2-valuedIncreasingDecreasingRandom
std::min_element23.26M/s23.26M/s23.15M/s22.94M/s22.94M/s
std::max_element23.26M/s23.26M/s23.15M/s22.94M/s22.62M/s
boost::first_min_element23.15M/s23.04M/s23.04M/s22.94M/s22.83M/s
boost::last_min_element23.26M/s23.26M/s23.26M/s22.83M/s16.23M/s
boost::first_max_element23.15M/s23.26M/s23.15M/s23.04M/s22.93M/s
boost::last_max_element23.26M/s23.15M/s23.15M/s22.94M/s16.18M/s
boost::minmax_element21.83M/s21.83M/s21.83M/s21.55M/s17.79M/s
boost::first_min_last_max_element18.52M/s18.38M/s18.38M/s18.94M/s16.29M/s
boost::last_min_first_max_element20.08M/s20.83M/s20.75M/s19.76M/s15.87M/s
boost::last_min_last_max_element18.66M/s19.69M/s19.69M/s19.23M/s15.77M/s
Number of elements per second for standard vector +container iterators
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
list<int>::iteratorIdentical2-valuedIncreasingDecreasingRandom
std::min_element5.8M/s5.8M/s5.80M/s5.73M/s5.73M/s
std::max_element5.81M/s5.81M/s5.78M/s5.73M/s5.75M/s
boost::first_min_element5.81M/s5.81M/s5.79M/s5.75M/s5.73M/s
boost::last_min_element5.81M/s5.80M/s5.79M/s5.73M/s5.03M/s
boost::first_max_element5.81M/s5.80M/s5.78M/s5.74M/s5.73M/s
boost::last_max_element5.81M/s5.80M/s5.79M/s5.73M/s5.07M/s
boost::minmax_element5.68M/s5.80M/s5.66M/s5.74M/s5.30M/s
boost::first_min_last_max_element5.79M/s5.81M/s5.78M/s5.73M/s5.04M/s
boost::last_min_first_max_element5.69M/s5.79M/s5.69M/s5.73M/s4.84M/s
boost::last_min_last_max_element5.61M/s5.79M/s5.64M/s5.74M/s4.75M/s
Runtimes for standard list container iterators
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
multiset<int>::iteratorIdentical2-valuedIncreasingDecreasingRandom
std::min_element4.03M/s4.04M/s4.02M/s4.04M/s2.97M/s
std::max_element3.007M4.02M/s4.02M/s4.01M/s4.02M/s2.96M/s
boost::first_min_element4.01M/s4.04M/s4.03M/s4.04M/s3.01M/s
boost::last_min_element4.03M/s4.04M/s4.04M/s4.04M/s3.00M/s
boost::first_max_element4.04M/s4.04M/s4.04M/s4.06M/s3.01M/s
boost::last_max_element4.04M/s4.04M/s4.03M/s4.04M/s3.00M/s
boost::minmax_element3.98M/s3.99M/s3.98M/s3.99M/s3.00M/s
boost::first_min_last_max_element3.99M/s3.98M/s3.97M/s3.99M/s2.99M/s
boost::last_min_first_max_element3.97M/s3.98M/s3.96M/s3.98M/s3.00M/s
boost::last_min_last_max_element4.00M/s4.00M/s4.00M/s4.02M/s2.97M/s
Runtimes for standard set/multiset container iterators
+ +
+
Last modified 2004-06-28 +

© Copyright Hervé +Brönnimann, Polytechnic University, 2002--2004. Permission to copy, use, +modify, sell and distribute this software and its documentation is granted +provided this copyright notice appears in all copies. This software and +its documentation is provided "as is" without express or implied warranty, +and with no claim as to its suitability for any purpose. + + diff --git a/minmax/doc/minmax_synopsis.html b/minmax/doc/minmax_synopsis.html new file mode 100644 index 0000000..c0895a5 --- /dev/null +++ b/minmax/doc/minmax_synopsis.html @@ -0,0 +1,114 @@ + + + + + + + + Boost minmax library synopsis + + + +

+

+Minmax_element

+ +

+Synopsis

+ +
namespace boost {
+
+  template <class T>
+  std::pair<const T&,const T&>
+  minmax(const T& a, const T& b);
+
+  template <class T, class BinaryPredicate>
+  std::pair<const T&,const T&>
+  minmax(const T& a, const T& b, BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  std::pair<ForwardIterator,ForwardIterator>
+  minmax_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  std::pair<ForwardIterator,ForwardIterator>
+  minmax_element(ForwardIterator first, ForwardIterator last,
+                 BinaryPredicate comp);
+
+
+  template <class ForwardIterator>
+  ForwardIterator first_min_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  ForwardIterator first_min_element(ForwardIterator first, ForwardIterator last,
+                                    BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  ForwardIterator last_min_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  ForwardIterator last_min_element(ForwardIterator first, ForwardIterator last,
+                                   BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  ForwardIterator first_max_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  ForwardIterator first_max_element(ForwardIterator first, ForwardIterator last,
+                                    BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  ForwardIterator last_max_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  ForwardIterator last_max_element(ForwardIterator first, ForwardIterator last,
+                                   BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  std::pair<ForwardIterator,ForwardIterator>
+  first_min_first_max_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  std::pair<ForwardIterator,ForwardIterator>
+  first_min_first_max_element(ForwardIterator first, ForwardIterator last,
+                             BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  std::pair<ForwardIterator,ForwardIterator>
+  first_min_last_max_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  std::pair<ForwardIterator,ForwardIterator>
+  first_min_last_max_element(ForwardIterator first, ForwardIterator last,
+                             BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  std::pair<ForwardIterator,ForwardIterator>
+  last_min_first_max_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  std::pair<ForwardIterator,ForwardIterator>
+  last_min_first_max_element(ForwardIterator first, ForwardIterator last,
+                             BinaryPredicate comp);
+
+  template <class ForwardIterator>
+  std::pair<ForwardIterator,ForwardIterator>
+  last_min_last_max_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  std::pair<ForwardIterator,ForwardIterator>
+  last_min_last_max_element(ForwardIterator first, ForwardIterator last,
+                            BinaryPredicate comp);
+
+}
+ +
+
Last modified 2002-05-09 +

© Copyright Hervé +Brönnimann, Polytechnic University, 2002. Permission to copy, use, +modify, sell and distribute this software and its documentation is granted +provided this copyright notice appears in all copies. This software and +its documentation is provided "as is" without express or implied warranty, +and with no claim as to its suitability for any purpose. + + diff --git a/minmax/example/Jamfile b/minmax/example/Jamfile new file mode 100644 index 0000000..75caeed --- /dev/null +++ b/minmax/example/Jamfile @@ -0,0 +1,14 @@ +# Boost.Minmax Library Example Jamfile +# +# Copyright (C) 2002--2004, Herve Bronnimann +# +# Use, modification, and distribution is subject to 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) +# + +subproject libs/minmax/example ; + +exe minmax_ex : minmax_ex.cpp ; +exe minmax_timer : minmax_timer.cpp ; + diff --git a/minmax/example/minmax_ex.cpp b/minmax/example/minmax_ex.cpp new file mode 100644 index 0000000..2ac9109 --- /dev/null +++ b/minmax/example/minmax_ex.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include + +#include + +int main() +{ + using namespace std; + list L; + generate_n(front_inserter(L), 1000, rand); + + typedef list::const_iterator iterator; + pair< iterator, iterator > result = boost::minmax_element(L.begin(), L.end()); + + cout << "The smallest element is " << *(result.first) << endl; + cout << "The largest element is " << *(result.second) << endl; + + assert( result.first == std::min_element(L.begin(), L.end()) ); + assert( result.second == std::max_element(L.begin(), L.end()) ); +} diff --git a/minmax/example/minmax_timer.cpp b/minmax/example/minmax_timer.cpp new file mode 100644 index 0000000..0a40d10 --- /dev/null +++ b/minmax/example/minmax_timer.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +// What's the proper BOOST_ flag for vs +#include + +#include +#include + +template +void tie(std::pair p, T1& min, T2& max) +{ + min = p.first; max = p.second; +} + +template +struct less_count : std::less { + less_count(less_count const& lc) : _M_counter(lc._M_counter) {} + less_count(int& counter) : _M_counter(counter) {} + bool operator()(Value const& a, Value const& b) const { + ++_M_counter; + return std::less::operator()(a,b); + } + void reset() { + _M_counter = 0; + } +private: + int& _M_counter; +}; + +inline int opt_min_count(int n) { + return (n==0) ? 0 : n-1; +} +inline int opt_minmax_count(int n) { + if (n < 2) return 0; + if (n == 2) return 1; + return (n%2 == 0) ? 3*(n/2)-1 : 3*(n/2)+1; +} +inline int opt_boost_minmax_count(int n) { + if (n < 2) return 0; + if (n == 2) return 1; + return (n%2 == 0) ? 3*(n/2)-2 : 3*(n/2); +} + +int repeats = 10; + +#define TIMER( n, cmd , cmdname ) \ + t.restart(); \ + for (int i=0; i +void test_minmax_element(CIterator first, CIterator last, int n, char* name) +{ + typedef typename std::iterator_traits::value_type vtype; + boost::timer t; + + std::cout << " ON " << name << " WITH OPERATOR<()\n"; + TIMER( n, std::min_element(first, last), + "std::min_element" << name << ""); + TIMER( n, std::max_element(first, last), + "std::max_element" << name << ""); + TIMER( n, boost::first_min_element(first, last), + "boost::first_min_element" << name << ""); + TIMER( n, boost::last_min_element(first, last), + "boost::last_min_element" << name << " "); + TIMER( n, boost::first_max_element(first, last), + "boost::first_max_element" << name << ""); + TIMER( n, boost::last_max_element(first, last), + "boost::last_max_element" << name << " "); + TIMER( n, boost::minmax_element(first, last), + "boost::minmax_element" << name << " "); + TIMER( n, boost::first_min_last_max_element(first, last), + "boost::first_min_last_max_element" << name << ""); + TIMER( n, boost::last_min_first_max_element(first, last), + "boost::last_min_first_max_element" << name << ""); + TIMER( n, boost::last_min_last_max_element(first, last), + "boost::last_min_last_max_element" << name << " "); + + #define pred std::bind2nd( std::greater(), vtype(10) ) + TIMER( n, boost::min_element_if(first, last, pred), + "boost::min_element_if" << name << ""); + TIMER( n, boost::max_element_if(first, last, pred), + "boost::max_element_if" << name << ""); + TIMER( n, std::min_element(boost::make_filter_iterator(first, last, pred), + boost::make_filter_iterator(last, last, pred)), + "std::min_element_with_filter_iterator" << name << ""); + TIMER( n, std::max_element(boost::make_filter_iterator(first, last, pred), + boost::make_filter_iterator(last, last, pred)), + "std::max_element_if_with_filter_iterator" << name << ""); + #undef pred + + int counter = 0; + less_count lc(counter); + std::cout << " ON " << name << " WITH LESS<> AND COUNTING COMPARISONS\n"; + CTIMER( n, std::min_element(first, last, lc), + "std::min_element" << name << " ", + counter, opt_min_count(n) ); + CTIMER( n, std::max_element(first, last, lc), + "std::max_element" << name << " ", + counter, opt_min_count(n) ); + CTIMER( n, boost::first_min_element(first, last, lc), + "boost::first_min_element" << name << "", + counter, opt_min_count(n) ); + CTIMER( n, boost::last_min_element(first, last, lc), + "boost::last_max_element" << name << " ", + counter, opt_min_count(n) ); + CTIMER( n, boost::first_max_element(first, last, lc), + "boost::first_min_element" << name << "", + counter, opt_min_count(n) ); + CTIMER( n, boost::last_max_element(first, last, lc), + "boost::last_max_element" << name << " ", + counter, opt_min_count(n) ); + CTIMER( n, boost::minmax_element(first, last, lc), + "boost::minmax_element" << name << " ", + counter, opt_minmax_count(n) ); + CTIMER( n, boost::first_min_last_max_element(first, last, lc), + "boost::first_min_last_max_element" << name << "", + counter, opt_boost_minmax_count(n) ); + CTIMER( n, boost::last_min_first_max_element(first, last, lc), + "boost::last_min_first_max_element" << name << "", + counter, opt_boost_minmax_count(n) ); + CTIMER( n, boost::last_min_last_max_element(first, last, lc), + "boost::last_min_last_max_element" << name << " ", + counter, opt_minmax_count(n) ); +} + +template +void test_container(Iterator first, Iterator last, int n, char* name) +{ + Container c(first, last); + typename Container::iterator fit(c.begin()), lit(c.end()); + test_minmax_element(fit, lit, n, name); +} + +template +void test_range(Iterator first, Iterator last, int n) +{ + typedef typename std::iterator_traits::value_type Value; + // Test various containers with these values + test_container< std::vector, Iterator, Value >(first, last, n, ""); +#ifndef ONLY_VECTOR + test_container< std::list, Iterator, Value >(first, last, n, " "); + test_container< std::multiset, Iterator, Value >(first, last, n, " "); +#endif +} + +template +void test(int n) +{ + // Populate test vector with identical values + std::cout << "IDENTICAL VALUES... \n"; + std::vector test_vector(n, Value(1)); + typename std::vector::iterator first( test_vector.begin() ); + typename std::vector::iterator last( test_vector.end() ); + test_range(first, last, n); + + // Populate test vector with two values + std::cout << "TWO DISTINCT VALUES...\n"; + typename std::vector::iterator middle( first + n/2 ); + std::fill(middle, last, Value(2)); + test_range(first, last, n); + + // Populate test vector with increasing values + std::cout << "INCREASING VALUES... \n"; + std::fill(first, last, Value(1)); + std::accumulate(first, last, Value(0)); + test_range(first, last, n); + + // Populate test vector with decreasing values + std::cout << "DECREASING VALUES... \n"; + std::reverse(first, last); + test_range(first, last, n); + + // Populate test vector with random values + std::cout << "RANDOM VALUES... \n"; + std::random_shuffle(first, last); + test_range(first, last, n); +} + +int +main(char argc, char** argv) +{ + int n = 100; + if (argc > 1) n = atoi(argv[1]); + if (argc > 2) repeats = atoi(argv[2]); + + test(n); + + return 0; +} diff --git a/minmax/index.html b/minmax/index.html new file mode 100644 index 0000000..45a6461 --- /dev/null +++ b/minmax/index.html @@ -0,0 +1,481 @@ + + + + + + + + + Boost Minmax library + + + +

Header <boost/minmax.hpp>

+ + + +Motivation
+Synopsis
+Function templates description
+Definition
+Requirements on types
+Preconditions
+Postconditions
+Complexity
+Example
+Notes
+Rationale
+Note about performance
+Acknowledgements +
+
+ + + +

+Motivation

+ +

The minmax library is composed of a single header <boost/minmax.hpp>. +I always thought it is a waste to have to call two functions to compute the +extent of a range, performing two passes over the input, when one should +be enough. The first part implements the function templates +minmax +and minmax_element as straightforward extensions of the C++ +standard. (Please note: the intent is not to fix the known defaults of +std::min +and std::max, but to add one more algorithms that combines both; see the +rationale.)

+ +

The second part proposes variants that can usually not be computed by +the minmax algorithm, and which are more flexible in case some elements are equal. +Those variants could have been also provided with policy-based design, +but I ruled against that (see rationale). +

+ +

If you are interested about +performance, +you will see that minmax_element is just slightly less efficient +than a single min_element or max_element, and thus +twice as efficient as two separate calls to min_element and +max_element. From a +theoretical standpoint, +all the minmax_element functions perform at most +3n/2+1 +comparisons and exactly n increments of the +ForwardIterator.

+ + + +

+Synopsis

+ +
namespace boost {
+
+  // Minmax
+
+  template <class T>
+  std::pair<const T&,const T&>
+  minmax(const T& a, const T& b);
+
+  template <class T, class BinaryPredicate>
+  std::pair<const T&,const T&>
+  minmax(const T& a, const T& b, BinaryPredicate comp);
+
+  // Minmax_element
+
+  template <class ForwardIterator>
+  std::pair<ForwardIterator,ForwardIterator>
+  minmax_element(ForwardIterator first, ForwardIterator last);
+
+  template <class ForwardIterator, class BinaryPredicate>
+  std::pair<ForwardIterator,ForwardIterator>
+  minmax_element(ForwardIterator first, ForwardIterator last,
+                 BinaryPredicate comp);
+
+}
+
+ +In addition, there are a bunch of extensions which specify +which element(s) you want to pick in case of equal elements. They are: +
    +
  • first_min_element and last_min_element
  • +
  • first_max_element and last_max_element
  • +
  • first_min_first_max_element, +first_min_last_max_element, +last_min_first_max_element, and +last_min_last_max_element
  • +
+I won't bore you with the complete synopsis, they have exactly the same +declaration as their corresponding _element function. Still, +you can find the complete synopsis here. + + + +

+Function templates description

+The minmax algorithm returns a pair p containing either +(a,b) +or (b,a), such that p.first<p.second in the first version, +or comp(p.first,p.second) in the second version. If the elements +are equivalent, the pair (a,b) is returned.
[1] +

The minmax_element is semantically equivalent to first_min_first_max_element. +

First_min_element and first_max_element find the smallest +and largest elements in the range [first, last). If there are +several instance of these elements, the first one is returned. They are +identical to +std::min_element and std::max_elementand +are only included in this library for symmetry. +

Last_min_element and last_max_element find the smallest +and largest elements in the range [first, last). They are almost +identical to +std::min_element and std::max_element, except +that they return the last instance of the largest element (and not the +first, as first_min_element and last_max_element would). +

The family of algorithms comprising first_min_first_max_element, +first_min_first_max_element, +first_min_first_max_element, +and first_min_first_max_element can be described generically as +follows (using which and +what for first +or last): which_min_what_max_element finds +the (first or last, according to which) smallest element +and the (first or last, according to what) largest element +in the range +[first, last). The first version is semantically +equivalent to: +

  std::make_pair(boost::which_min_element(first,last),
+                 boost::what_max_element(first,last)),
+and the second version to: +
  std::make_pair(boost::which_min_element(first,last,comp),
+                 boost::what_max_element(first,last,comp)).
+ +


Note: the first_min_last_max_element can also be described +as finding the first and last elements in the range if it were stably sorted. + + + +

+Definition

+Defined in minmax.hpp. + + + +

+Requirements on types

+For minmax, T must be a model of
LessThan +Comparable. +

For all the other function templates, versions with two template parameters: +

+For the versions with three template parameters: +
    +
  • +ForwardIterator is a model of Forward +Iterator.
  • + +
  • +BinaryPredicate is a model of Binary +Predicate.
  • + +
  • +ForwardIterator's value type is convertible to BinaryPredicate's +first argument type and second argument type.
  • +
+ + + +

+Preconditions

+ +
    +
  • +[first, last) is a valid range.
  • +
+
+ + +

+Postconditions

+In addition to the semantic description above. for minmax_element +and all the which_min_what_max_element +variants, the return value is +last or std::make_pair(last,last) +if and only if [first, last) is an empty range. Otherwise, the +return value or both members of the resulting pair are iterators in the +range +[first, last). +
+ + +

+Complexity

+Minmax performs a single comparison and is otherwise of constant complexity. +

The complexity of all the other algorithms is linear. They all perform +exactly n increment operations, and zero comparisons if [first,last) +is empty, otherwise : +

    +
  • +all the min_element and max_element variants perform +exactly(n-1) comparisons,
  • + +
  • +minmax_element , first_min_first_max_element, and last_min_last_max_element +perform at most 3(n/2)-1 comparisons if n is even and +non-zero, and at most 3(n/2)+1 if n is odd, +[2]
  • + +
  • +first_min_last_max_element, and last_min_first_max_element +perform exactly 3n/2-2 comparisons if n is even and non-zero, +and at most 3(n/2) if n is odd, +[3]
  • +
+where n is the number of elements in [first,last). + + + +

+Example

+This example is included in the distribution in the examples section of +the library under +
minmax_ex.cpp. + +
int main()
+{
+  using namespace std;
+  list<int> L;
+  generate_n(front_inserter(L), 1000, rand);
+
+  typedef list<int>::const_iterator iterator;
+  pair< iterator, iterator > result = boost::minmax_element(L.begin(), L.end());
+  cout << "The smallest element is " << *(result.first) << endl;
+  cout << "The largest element is  " << *(result.second) << endl;
+  assert( result.first  == std::min_element(L.begin(), L.end());
+  assert( result.second == std::max_element(L.begin(), L.end());
+}
+ + + +

+Notes

+
[1] It is not recommended to use +idioms such as tie(a,b)=minmax(a,b) +to order two elements a, b, although this would have +the desired effect. The reason is that two unnecessary assignments are +performed if a and b are in order. It is better to stick to if (b<a) +swap(a,b) to achieve that effect. +

[2] These algorithms always +perform at least 3n/2-2 comparisons, which is a lower bound on +the number of comparisons in any case (Cormen, Leiserson, Rivest: "Introduction +to Algorithms", section 9.1, Exercise 9.1-). The algorithms essentially compare +the elements in pairs, performing 1 comparison for the first two elements, +then 3 comparisons for each remaining pair of elements (one to order the +elements and one for updating each the minimum and and the maximum). When +the number of elements is odd, the last one needs to be compared to the +current minimum and also to the current maximum. In addition, for minmax, +in cases where equality of the two members in the pair could occur, and +the update stores the second, we save the first to check at the end if +the update should have stored the first (in case of equality). It's hard +to predict if the last comparison is performed or not, hence the at most +in both cases. +

[3] These algorithms always +perform at least 3n/2-2 comparisons, which is a lower bound on +the number of comparisons in any case. The method is the same as in note +[2] +above, and like above, when the number of elements is odd, the last one +needs to be compared to the current minimum and also to the current maximum. +We can avoid the latter comparison if the former is successful, hence the +at +most instead of exactly in the odd case. + + + +

+Rationale:

+ + +

Your minmax suffers from the same problems as std::min and +std::max.

+

I am aware of the problems with std::min and +std::max, and all the debate that has been going on (please consult +Alexandrescu's +paper and the links therein). But I don't see the purpose of this +library as fixing something that is part of the C++ standard. I humbly +think it's beyond the scope of this library. Rather, I am +following the way of the standard in simply providing one more function +of the same family. If someone else wants to fix std::min, their fix +would probably apply to boost::minmax as well.

+ + +

Why no min/max_element_if?

+

In a first version of the library, I proposed _if versions of +all the algorithms (well, not all, because that would be too much). +However, there is simply no reason to do so, and all the versions I had +were just as fast implemented using the excellent +<boost/iterator_adaptors.hpp> library. Namely, a call to +min_element_if(first, last, pred) would be just as well +implemented by: +

+     // equivalent to min_element_if(first, last, pred)
+     min_element(boost::make_filter_iterator(first, last, pred),
+                 boost::make_filter_iterator(last, last, pred));
+
+Arguably, the min_element_if version is somewhat shorter, but +the overhead of iterator adaptors is not large, and they get rid of a +lot of code (think of all the combinations between first/last and +doubling them with _if variants!).

+ +

Discussion: about std::max_element

+

This rationale is somewhat historical, but explains why there are all +these first/last_min/max_element functions.

+

The C++ standard mandates that std::min_element and std::max_element +return the first instance of the smallest and largest elements (as opposed +to, say, the last). This arbitrary choice has some consistency: In the +case of v of type vector<int>, for instance, it is true that std::min_element(v.begin(),v.end(),std::less<int>()) +== std::max_element(v.begin(),v.end(),std::greater<int>()). +

There is of course nothing wrong with this: it's simply a matter of +choice. Yet another way to specify min_element and max_element is to define +them as the first and the last elements if the range was stably sorted. +(The stable sort is necessary to disambiguate between iterators +that have the same value.) In that case, min should return the first instance +and max should return the last. Then, both functions are related by +reverse_iterator(std::first_min_element(v.begin(),v.end(),std::less<int>())) +== +std::last_max_element(v.rbegin(),v.rend(),std::greater<int>()). +This definition is subtly different from the previous one.

+

The definition problem surfaces when one tries to design a minmax_element, +using the procedure proposed in (Cormen, Leiserson, Rivest: "Introduction +to Algorithms", section 9.1). It should be possible to derive an +algorithm using only 3n/2 comparisons if [first,last) has +n +elements, but if one tries to write a function called first_min_first_max_element() +which returns both std::min_element and std::max_element +in a pair, the trivial implementation does not work. The problem, rather +subtly, is about equal elements: I had to think for a while to find a +way to perform only three +comparisons per pair and return the first min and first max elements. +For a long time, it seemed any +attempts at doing so would consume four comparisons per pair in the worst +case. This implementation achieves that.

+

It is not possible (or even desirable) to change the meaning of +max_element, +but it is still beneficial to provide a function called minmax_element, +which returns a pair of min_element and max_element. +Although it is easy enough to call min_element and max_element, +this performs +2(n-1) comparisons, and necessitates two +passes over the input. In contrast, +minmax_element will perform +the fewer comparisons and perform a single pass over the input. +The savings can be significant when the iterator type is not a raw pointer, +or even is just a model of the InputIterator concept (although in that +case the interface would have to be +changed, as the return type could not be copied, so one could e.g. +return a value).

+

In order to benefit from all the variants of the algorithm, I propose +to introduce both first_min_element and last_min_element, +and their counterparts first_max_element and last_max_element. +Then I also propose all the variants algorithms: first_min_last_max_element +and last_min_first_max_element, which perform only at most 3n/2 +comparisons, and only a single pass on the input. In fact, it can be proven +that computing minmax requires at least 3(n/2)-2 comparisons in +any instance of the problem (Cormen, Leiserson, Rivest, 2nd edition, section +9.1). The implementation I give does not perform unnecessary comparisons +(whose result could have been computed by transitivity from previous +comparisons).

+

It appears that first_min_last_max_element may be just a tad +slower than +first_min_element alone, still much less than first_min_element +and +last_max_element called separately. [2] + +

Why algorithms and not accumulators?

+

The minmax algorithms are useful in computing the extent of a range. +In computer graphics, we need a bounding box of a set of objects. +In that case the need for a single pass is even more stringent +as all three directions must be done at once. Food for thoughts: there +is matter for a nice generic programming library with stackable update_min +and update_max function objects which store a reference to the +min_resultand +max_result variables, in conjunction with the for_each +algorithm).

+

I believe many standard sequential algorithms could be reformulated +with accumulators (and many others, such as in statistics, expectation / +variance / etc.). It seems that there is room for another library, but I +do not see it competing with minmax, rather extending several algorithms +(including minmax) to the accumulator framework. However, I felt it is +beyond the scope of this library to provide such accumulators.

+ + +

This first/last is a perfect application for a policy-based +design.

+

True, and I could have gone that way, with the default policy for +min_element and max_element to pick the first +occurence of the result. This would have thinned the number of +combinations of the minmax_element variants. But it would also have +meant to change the interface of boost::minmax_element. +One of the goals of the minmax_element algorithm is its +eventual addition to the C++ standard, in connection with +std::min_element and std::max_element +(and I feel that it would be quite natural +given the shortness of the implementation, and the not quite trivial +detail which is needed to get it right). So changing the interface by +adding policies would have meant unfortunately to depart from the +standard and created an obstacle towards that goal. Besides, the code +remains rather readable and simple without policies. So I am quite happy +to keep it like this. +

+ + + +

About performance

+ + + +

+Acknowledgements

+My students in CS903 (Polytechnic Univ.,
http://photon.poly.edu/~hbr/cs903/) +who had minmax_element as an assignment helped clarify the issues, +and also come up with the optimum number of comparisons for first_min_last_max_element. +The identification of the issue surrounding max_element is solely +my own. +

One minmax_element implementation, which performs 3(n/2)+O(log +n) comparisons on the average when the elements are random_shuffled, +was suggested by my student Marc Glisse. The current one, which performs +3(n/2)+1 +comparisons in the worst case, was suggested by John Iacono.

+

Finally, Matthew Wilson and Jeremy Siek contributed pre-review +comments, while Gennadiy Rozental, John Maddock, Craig Henderson, Gary +Powell participated in the review of the library, managed by Thomas +Witt. In particular, Gennadiy suggested a factorization of the code; +while I haven't followed it all the way, his suggestions do make the +code more readable and still work with older compilers. +All my thanks for the excellent advice and reviews from all. +

+See also

+min, max, +min_element, +max_element, +LessThan +Comparable, +sort, +nth_element +. +
+
Last modified 2002-06-28 +

© Copyright Hervé +Brönnimann, Polytechnic University, 2002--2004. +Use, modification, and distribution is subject to 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) + + + diff --git a/minmax/test/Jamfile b/minmax/test/Jamfile new file mode 100644 index 0000000..735622d --- /dev/null +++ b/minmax/test/Jamfile @@ -0,0 +1,23 @@ +# Boost.Minmax Library Test Jamfile +# +# Copyright (C) 2002--2004, Herve Bronnimann +# +# Use, modification, and distribution is subject to 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) +# + +subproject libs/minmax/test ; + +# bring in rules for testing +SEARCH on testing.jam = $(BOOST_BUILD_PATH) ; +include testing.jam ; + +# Make tests run by default. +DEPENDS all : test ; + +{ + test-suite minmax: + [ run minmax_test.cpp ] + ; +} diff --git a/minmax/test/Jamfile.v2 b/minmax/test/Jamfile.v2 new file mode 100644 index 0000000..95b1c68 --- /dev/null +++ b/minmax/test/Jamfile.v2 @@ -0,0 +1,17 @@ +# Boost.Minmax Library test Jamfile +# +# Copyright (C) 2002--2004, Herve Bronnimann +# +# Use, modification, and distribution is subject to 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) +# + +import testing ; + +{ + test-suite minmax: + [ run minmax_test.cpp ] + ; +} + diff --git a/minmax/test/minmax_test.cpp b/minmax/test/minmax_test.cpp new file mode 100644 index 0000000..9cc0f59 --- /dev/null +++ b/minmax/test/minmax_test.cpp @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class custom { + int _M_x; + friend bool operator<(custom const& x, custom const& y); + friend std::ostream& operator<<(std::ostream& str, custom const& x); +public: + explicit custom(int x = 0) : _M_x(x) {} + custom(custom const& y) : _M_x(y._M_x) {} + custom operator+(custom const& y) const { return custom(_M_x+y._M_x); } + custom& operator+=(custom const& y) { _M_x += y._M_x; return *this; } +}; + +bool operator< (custom const& x, custom const& y) +{ + return x._M_x < y._M_x; +} + +std::ostream& operator<<(std::ostream& str, custom const& x) +{ + str << x._M_x; + return str; +} + +namespace std { + +template <> +struct iterator_traits { + typedef random_access_iterator_tag iterator_category; + typedef int value_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef value_type& reference; +}; + +template <> +struct iterator_traits { + typedef random_access_iterator_tag iterator_category; + typedef custom value_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef value_type& reference; +}; + +} + +template +void tie(std::pair p, T3& first, T4& second) +{ + first = T3(p.first); second = T4(p.second); +} + +template +struct less_count : std::less { + typedef std::less Base; + less_count(less_count const& lc) : _M_counter(lc._M_counter) {} + less_count(int& counter) : _M_counter(counter) {} + bool operator()(Value const& a, Value const& b) const { + ++_M_counter; + return Base::operator()(a,b); + } + void reset() { + _M_counter = 0; + } +private: + int& _M_counter; +}; + +inline int opt_min_count(int n) { + return (n==0) ? 0 : n-1; +} +inline int opt_minmax_count(int n) { + if (n < 2) return 0; + if (n == 2) return 2; + return (n%2 == 0) ? 3*(n/2)-1 : 3*(n/2)+1; +} +inline int opt_boost_minmax_count(int n) { + if (n < 2) return 0; + if (n == 2) return 1; + return (n%2 == 0) ? 3*(n/2)-2 : 3*(n/2); +} + +#define CHECK_EQUAL_ITERATORS( left, right, first ) \ +BOOST_CHECK_EQUAL( std::distance( first, left ), std::distance( first, right ) ) + +template +void test_minmax(CIterator first, CIterator last, int n) +{ + using namespace boost; + + typedef typename std::iterator_traits::value_type Value; + typedef boost::reverse_iterator RCIterator; + // assume that CIterator is BidirectionalIter + CIterator min, max; + RCIterator rfirst(last), rlast(first), rmin(min), rmax(max); + int counter = 0; + less_count lc(counter); + + // standard extensions + // first version, operator< + tie( boost::minmax_element(first, last), min, max ); + + CHECK_EQUAL_ITERATORS( min, std::min_element(first, last), first ); + CHECK_EQUAL_ITERATORS( max, std::max_element(first, last), first ); + // second version, comp function object (keeps a counter!) + lc.reset(); + tie( boost::minmax_element(first, last, lc), min, max ); + BOOST_CHECK( counter <= opt_minmax_count(n) ); + CHECK_EQUAL_ITERATORS( min, std::min_element(first, last, lc), first ); + CHECK_EQUAL_ITERATORS( max, std::max_element(first, last, lc), first ); + + // boost extensions + // first version, operator< + CHECK_EQUAL_ITERATORS( boost::first_min_element(first, last), std::min_element(first, last), first ); + rmin = RCIterator(boost::last_min_element(first, last)); + rmin = (rmin == rfirst) ? rlast : --rmin; + CHECK_EQUAL_ITERATORS( rmin, std::min_element(rfirst, rlast), rfirst ); + CHECK_EQUAL_ITERATORS( boost::first_max_element(first, last), std::max_element(first, last), first ); + rmax = RCIterator(boost::last_max_element(first, last)); + rmax = (rmax == rfirst) ? rlast : --rmax; + CHECK_EQUAL_ITERATORS( rmax, std::max_element(rfirst, rlast), rfirst ); + tie( boost::first_min_last_max_element(first, last), min, max ); + CHECK_EQUAL_ITERATORS( min, boost::first_min_element(first, last), first ); + CHECK_EQUAL_ITERATORS( max, boost::last_max_element(first, last), first ); + tie( boost::last_min_first_max_element(first, last), min, max ); + CHECK_EQUAL_ITERATORS( min, boost::last_min_element(first, last), first ); + CHECK_EQUAL_ITERATORS( max, boost::first_max_element(first, last), first ); + tie( boost::last_min_last_max_element(first, last), min, max ); + CHECK_EQUAL_ITERATORS( min, boost::last_min_element(first, last), first ); + CHECK_EQUAL_ITERATORS( max, boost::last_max_element(first, last), first ); + // second version, comp function object (keeps a counter!) + lc.reset(); + min = boost::first_min_element(first, last, lc); + BOOST_CHECK( counter <= opt_min_count(n) ); + CHECK_EQUAL_ITERATORS( min, std::min_element(first, last, lc), first ); + lc.reset(); + rmin = RCIterator(boost::last_min_element(first, last, lc)); + rmin = (rmin == rfirst) ? rlast : --rmin; + BOOST_CHECK( counter <= opt_min_count(n) ); + CHECK_EQUAL_ITERATORS( rmin, std::min_element(rfirst, rlast, lc), rfirst ); + lc.reset(); + max = boost::first_max_element(first, last, lc); + BOOST_CHECK( counter <= opt_min_count(n) ); + CHECK_EQUAL_ITERATORS( max, std::max_element(first, last, lc), first ); + lc.reset(); + rmax = RCIterator(boost::last_max_element(first, last, lc)); + rmax = (rmax == rfirst) ? rlast : --rmax; + BOOST_CHECK( counter <= opt_min_count(n) ); + CHECK_EQUAL_ITERATORS( rmax, std::max_element(rfirst, rlast, lc), rfirst ); + lc.reset(); + tie( boost::first_min_last_max_element(first, last, lc), min, max ); + BOOST_CHECK( counter <= opt_boost_minmax_count(n) ); + CHECK_EQUAL_ITERATORS( min, boost::first_min_element(first, last, lc), first ); + CHECK_EQUAL_ITERATORS( max, boost::last_max_element(first, last, lc), first ); + lc.reset(); + tie( boost::last_min_first_max_element(first, last, lc), min, max ); + BOOST_CHECK( counter <= opt_boost_minmax_count(n) ); + BOOST_CHECK( min == boost::last_min_element(first, last, lc) ); + CHECK_EQUAL_ITERATORS( max, boost::first_max_element(first, last, lc), first ); + lc.reset(); + tie( boost::last_min_last_max_element(first, last, lc), min, max ); + BOOST_CHECK( counter <= opt_minmax_count(n) ); + CHECK_EQUAL_ITERATORS( min, boost::last_min_element(first, last, lc), first ); + CHECK_EQUAL_ITERATORS( max, boost::last_max_element(first, last, lc), first ); +} + +template +void test_container(Iterator first, Iterator last, int n, Container* dummy = 0 ) +{ + Container c(first, last); + test_minmax(c.begin(), c.end(), n); +} + +template +void test_range(Iterator first, Iterator last, int n, char* name) +{ + typedef typename std::iterator_traits::value_type Value; + // Test various containers with these values + // std::cout << " vector<" << name << ">..."; + test_container< std::vector, Iterator, Value >(first, last, n); + // std::cout << " list<" << name << ">..."; + test_container< std::list, Iterator, Value >(first, last, n); + // std::cout << " set<" << name << ">..."; + test_container< std::set, Iterator, Value >(first, last, n); + // std::cout << "\n"; +} + +template +void test(int n, char* name) +{ + // Populate test vector with identical values + // std::cout << " Identical values... "; + std::vector test_vector(n, Value(1)); + typename std::vector::iterator first( test_vector.begin() ); + typename std::vector::iterator last( test_vector.end() ); + test_range(first, last, n, name); + + // Populate test vector with two values + // std::cout << " Two distinct values..."; + typename std::vector::iterator middle( first + n/2 ); + std::fill(middle, last, Value(2)); + test_range(first, last, n, name); + + // Populate test vector with increasing values + // std::cout << " Increasing values... "; + std::accumulate(first, last, Value(0)); + test_range(first, last, n, name); + + // Populate test vector with decreasing values + // std::cout << " Decreasing values... "; + std::reverse(first, last); + test_range(first, last, n, name); + + // Populate test vector with random values + // std::cout << " Random values... "; + std::random_shuffle(first, last); + test_range(first, last, n, name); +} + +int test_main( int argc, char* argv[] ) +{ + int n = 100; + if (argc > 1) n = atoi(argv[1]); + + test(n, "builtin"); + test(n, "custom "); + + return 0; +}