339 lines
11 KiB
C++
339 lines
11 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// 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/accumulators_fwd.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)
|
|
|
|
BOOST_ACCUMULATORS_IGNORE_GLOBAL(right_tail_cache_size)
|
|
BOOST_ACCUMULATORS_IGNORE_GLOBAL(left_tail_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
|