/////////////////////////////////////////////////////////////////////////////// | |
// tail.hpp | |
// | |
// Copyright 2005 Eric Niebler, Michael Gauckler. Distributed under the Boost | |
// Software License, Version 1.0. (See accompanying file | |
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
#ifndef BOOST_ACCUMULATORS_STATISTICS_TAIL_HPP_EAN_28_10_2005 | |
#define BOOST_ACCUMULATORS_STATISTICS_TAIL_HPP_EAN_28_10_2005 | |
#include <vector> | |
#include <functional> | |
#include <boost/assert.hpp> | |
#include <boost/range.hpp> | |
#include <boost/mpl/if.hpp> | |
#include <boost/mpl/or.hpp> | |
#include <boost/mpl/placeholders.hpp> | |
#include <boost/parameter/keyword.hpp> | |
#include <boost/iterator/reverse_iterator.hpp> | |
#include <boost/iterator/permutation_iterator.hpp> | |
#include <boost/accumulators/framework/accumulator_base.hpp> | |
#include <boost/accumulators/framework/extractor.hpp> | |
#include <boost/accumulators/numeric/functional.hpp> | |
#include <boost/accumulators/framework/parameters/sample.hpp> | |
#include <boost/accumulators/framework/depends_on.hpp> | |
#include <boost/accumulators/statistics_fwd.hpp> | |
namespace boost { namespace accumulators | |
{ | |
/////////////////////////////////////////////////////////////////////////////// | |
// cache_size named parameters | |
BOOST_PARAMETER_NESTED_KEYWORD(tag, right_tail_cache_size, cache_size) | |
BOOST_PARAMETER_NESTED_KEYWORD(tag, left_tail_cache_size, cache_size) | |
namespace detail | |
{ | |
/////////////////////////////////////////////////////////////////////////////// | |
// tail_range | |
/// INTERNAL ONLY | |
/// | |
template<typename ElementIterator, typename IndexIterator> | |
struct tail_range | |
{ | |
typedef boost::iterator_range< | |
boost::reverse_iterator<boost::permutation_iterator<ElementIterator, IndexIterator> > | |
> type; | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// make_tail_range | |
/// INTERNAL ONLY | |
/// | |
template<typename ElementIterator, typename IndexIterator> | |
typename tail_range<ElementIterator, IndexIterator>::type | |
make_tail_range(ElementIterator elem_begin, IndexIterator index_begin, IndexIterator index_end) | |
{ | |
return boost::make_iterator_range( | |
boost::make_reverse_iterator( | |
boost::make_permutation_iterator(elem_begin, index_end) | |
) | |
, boost::make_reverse_iterator( | |
boost::make_permutation_iterator(elem_begin, index_begin) | |
) | |
); | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// stat_assign_visitor | |
/// INTERNAL ONLY | |
/// | |
template<typename Args> | |
struct stat_assign_visitor | |
{ | |
stat_assign_visitor(Args const &a, std::size_t i) | |
: args(a) | |
, index(i) | |
{ | |
} | |
template<typename Stat> | |
void operator ()(Stat &stat) const | |
{ | |
stat.assign(this->args, this->index); | |
} | |
private: | |
stat_assign_visitor &operator =(stat_assign_visitor const &); | |
Args const &args; | |
std::size_t index; | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// stat_assign | |
/// INTERNAL ONLY | |
/// | |
template<typename Args> | |
inline stat_assign_visitor<Args> const stat_assign(Args const &args, std::size_t index) | |
{ | |
return stat_assign_visitor<Args>(args, index); | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// is_tail_variate_feature | |
/// INTERNAL ONLY | |
/// | |
template<typename Stat, typename LeftRight> | |
struct is_tail_variate_feature | |
: mpl::false_ | |
{ | |
}; | |
/// INTERNAL ONLY | |
/// | |
template<typename VariateType, typename VariateTag, typename LeftRight> | |
struct is_tail_variate_feature<tag::tail_variate<VariateType, VariateTag, LeftRight>, LeftRight> | |
: mpl::true_ | |
{ | |
}; | |
/// INTERNAL ONLY | |
/// | |
template<typename LeftRight> | |
struct is_tail_variate_feature<tag::tail_weights<LeftRight>, LeftRight> | |
: mpl::true_ | |
{ | |
}; | |
} // namespace detail | |
namespace impl | |
{ | |
/////////////////////////////////////////////////////////////////////////////// | |
// tail_impl | |
template<typename Sample, typename LeftRight> | |
struct tail_impl | |
: accumulator_base | |
{ | |
// LeftRight must be either right or left | |
BOOST_MPL_ASSERT(( | |
mpl::or_<is_same<LeftRight, right>, is_same<LeftRight, left> > | |
)); | |
typedef | |
typename mpl::if_< | |
is_same<LeftRight, right> | |
, numeric::functional::greater<Sample const, Sample const> | |
, numeric::functional::less<Sample const, Sample const> | |
>::type | |
predicate_type; | |
// for boost::result_of | |
typedef typename detail::tail_range< | |
typename std::vector<Sample>::const_iterator | |
, std::vector<std::size_t>::iterator | |
>::type result_type; | |
template<typename Args> | |
tail_impl(Args const &args) | |
: is_sorted(false) | |
, indices() | |
, samples(args[tag::tail<LeftRight>::cache_size], args[sample | Sample()]) | |
{ | |
this->indices.reserve(this->samples.size()); | |
} | |
tail_impl(tail_impl const &that) | |
: is_sorted(that.is_sorted) | |
, indices(that.indices) | |
, samples(that.samples) | |
{ | |
this->indices.reserve(this->samples.size()); | |
} | |
// This just stores the heap and the samples. | |
// In operator()() below, if we are adding a new sample | |
// to the sample cache, we force all the | |
// tail_variates to update also. (It's not | |
// good enough to wait for the accumulator_set to do it | |
// for us because then information about whether a sample | |
// was stored and where is lost, and would need to be | |
// queried at runtime, which would be slow.) This is | |
// implemented as a filtered visitation over the stats, | |
// which we can access because args[accumulator] gives us | |
// all the stats. | |
template<typename Args> | |
void operator ()(Args const &args) | |
{ | |
if(this->indices.size() < this->samples.size()) | |
{ | |
this->indices.push_back(this->indices.size()); | |
this->assign(args, this->indices.back()); | |
} | |
else if(predicate_type()(args[sample], this->samples[this->indices[0]])) | |
{ | |
std::pop_heap(this->indices.begin(), this->indices.end(), indirect_cmp(this->samples)); | |
this->assign(args, this->indices.back()); | |
} | |
} | |
result_type result(dont_care) const | |
{ | |
if(!this->is_sorted) | |
{ | |
// Must use the same predicate here as in push_heap/pop_heap above. | |
std::sort_heap(this->indices.begin(), this->indices.end(), indirect_cmp(this->samples)); | |
// sort_heap puts elements in reverse order. Calling std::reverse | |
// turns the sorted sequence back into a valid heap. | |
std::reverse(this->indices.begin(), this->indices.end()); | |
this->is_sorted = true; | |
} | |
return detail::make_tail_range( | |
this->samples.begin() | |
, this->indices.begin() | |
, this->indices.end() | |
); | |
} | |
private: | |
struct is_tail_variate | |
{ | |
template<typename T> | |
struct apply | |
: detail::is_tail_variate_feature< | |
typename detail::feature_tag<T>::type | |
, LeftRight | |
> | |
{}; | |
}; | |
template<typename Args> | |
void assign(Args const &args, std::size_t index) | |
{ | |
BOOST_ASSERT(index < this->samples.size()); | |
this->samples[index] = args[sample]; | |
std::push_heap(this->indices.begin(), this->indices.end(), indirect_cmp(this->samples)); | |
this->is_sorted = false; | |
// Tell the tail variates to store their values also | |
args[accumulator].template visit_if<is_tail_variate>(detail::stat_assign(args, index)); | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
struct indirect_cmp | |
: std::binary_function<std::size_t, std::size_t, bool> | |
{ | |
indirect_cmp(std::vector<Sample> const &s) | |
: samples(s) | |
{ | |
} | |
bool operator ()(std::size_t left, std::size_t right) const | |
{ | |
return predicate_type()(this->samples[left], this->samples[right]); | |
} | |
private: | |
indirect_cmp &operator =(indirect_cmp const &); | |
std::vector<Sample> const &samples; | |
}; | |
mutable bool is_sorted; | |
mutable std::vector<std::size_t> indices; | |
std::vector<Sample> samples; | |
}; | |
} // namespace impl | |
// TODO The templatized tag::tail below should inherit from the correct named parameter. | |
// The following lines provide a workaround, but there must be a better way of doing this. | |
template<typename T> | |
struct tail_cache_size_named_arg | |
{ | |
}; | |
template<> | |
struct tail_cache_size_named_arg<left> | |
: tag::left_tail_cache_size | |
{ | |
}; | |
template<> | |
struct tail_cache_size_named_arg<right> | |
: tag::right_tail_cache_size | |
{ | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// tag::tail<> | |
// | |
namespace tag | |
{ | |
template<typename LeftRight> | |
struct tail | |
: depends_on<> | |
, tail_cache_size_named_arg<LeftRight> | |
{ | |
/// INTERNAL ONLY | |
/// | |
typedef accumulators::impl::tail_impl<mpl::_1, LeftRight> impl; | |
#ifdef BOOST_ACCUMULATORS_DOXYGEN_INVOKED | |
/// tag::tail<LeftRight>::cache_size named parameter | |
static boost::parameter::keyword<tail_cache_size_named_arg<LeftRight> > const cache_size; | |
#endif | |
}; | |
struct abstract_tail | |
: depends_on<> | |
{ | |
}; | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// extract::tail | |
// | |
namespace extract | |
{ | |
extractor<tag::abstract_tail> const tail = {}; | |
BOOST_ACCUMULATORS_IGNORE_GLOBAL(tail) | |
} | |
using extract::tail; | |
template<typename LeftRight> | |
struct feature_of<tag::tail<LeftRight> > | |
: feature_of<tag::abstract_tail> | |
{ | |
}; | |
}} // namespace boost::accumulators | |
#endif |