From 6df2923b40ae6ecf58b4225cf1eac70c6259a613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Br=C3=B6nnimann?= Date: Thu, 1 Jul 2004 18:31:21 +0000 Subject: [PATCH] Remove these files from the libs/directory, and reenter them later in the libs/algorithm/ subdir. --Herve' [SVN r23304] --- minmax/doc/minmax_benchs.html | 524 -------------------------------- minmax/doc/minmax_synopsis.html | 114 ------- minmax/example/Jamfile | 14 - minmax/example/minmax_ex.cpp | 23 -- minmax/example/minmax_timer.cpp | 207 ------------- minmax/index.html | 481 ----------------------------- minmax/test/Jamfile | 23 -- minmax/test/Jamfile.v2 | 17 -- minmax/test/minmax_test.cpp | 241 --------------- 9 files changed, 1644 deletions(-) delete mode 100644 minmax/doc/minmax_benchs.html delete mode 100644 minmax/doc/minmax_synopsis.html delete mode 100644 minmax/example/Jamfile delete mode 100644 minmax/example/minmax_ex.cpp delete mode 100644 minmax/example/minmax_timer.cpp delete mode 100644 minmax/index.html delete mode 100644 minmax/test/Jamfile delete mode 100644 minmax/test/Jamfile.v2 delete mode 100644 minmax/test/minmax_test.cpp diff --git a/minmax/doc/minmax_benchs.html b/minmax/doc/minmax_benchs.html deleted file mode 100644 index 15fc9e1..0000000 --- a/minmax/doc/minmax_benchs.html +++ /dev/null @@ -1,524 +0,0 @@ - - - - - - - - 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 deleted file mode 100644 index c0895a5..0000000 --- a/minmax/doc/minmax_synopsis.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - 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 deleted file mode 100644 index 75caeed..0000000 --- a/minmax/example/Jamfile +++ /dev/null @@ -1,14 +0,0 @@ -# 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 deleted file mode 100644 index 2ac9109..0000000 --- a/minmax/example/minmax_ex.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#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 deleted file mode 100644 index 0a40d10..0000000 --- a/minmax/example/minmax_timer.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#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 deleted file mode 100644 index 45a6461..0000000 --- a/minmax/index.html +++ /dev/null @@ -1,481 +0,0 @@ - - - - - - - - - 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: - -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: - - - - -

-Preconditions

- - -
- - -

-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 : -

-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 deleted file mode 100644 index 735622d..0000000 --- a/minmax/test/Jamfile +++ /dev/null @@ -1,23 +0,0 @@ -# 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 deleted file mode 100644 index 95b1c68..0000000 --- a/minmax/test/Jamfile.v2 +++ /dev/null @@ -1,17 +0,0 @@ -# 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 deleted file mode 100644 index 9cc0f59..0000000 --- a/minmax/test/minmax_test.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#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; -}