mirror of
https://github.com/boostorg/container.git
synced 2026-08-04 03:34:11 +02:00
Add predicate calling limit tests to all algorithms
This commit is contained in:
@@ -272,6 +272,66 @@ void test_all_of_forward_failure_before_last_segment()
|
||||
BOOST_TEST(segmented_all_of(sv.begin(), last, not_equals_val(80)));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.all.of] mandates "At most last - first applications of the predicate".
|
||||
// The lower bound is not in the standard but follows from the answer: false
|
||||
// cannot be returned without having applied pred to every element up to and
|
||||
// including the first failing one, and true cannot be returned without having
|
||||
// applied it to all of them. Bracketing the count catches an element retested
|
||||
// when the scan crosses a segment boundary.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct all_of_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit all_of_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const boost::container::vector<int> flat = test_detail::flatten_n_ints(c, n);
|
||||
std::size_t needed = n;
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
if(!pred(flat[i])) { needed = i + 1u; break; }
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_all_of(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_all_of_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, all_of_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, all_of_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_all_of_predicate_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_all_of_count_shapes(vals, n, not_equals_val((v <= n) ? int(v) : -999));
|
||||
run_all_of_count_shapes(vals, n, not_multiple_of(1));
|
||||
run_all_of_count_shapes(vals, n, not_multiple_of(3));
|
||||
run_all_of_count_shapes(vals, n, is_positive());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_all_of_shape_matrix();
|
||||
@@ -285,5 +345,6 @@ int main()
|
||||
test_all_of_single_segment_seg2_single_inner();
|
||||
test_all_of_single_segment_forward();
|
||||
test_all_of_forward_failure_before_last_segment();
|
||||
test_all_of_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -272,6 +272,66 @@ void test_any_of_forward_hit_before_last_segment()
|
||||
BOOST_TEST(!segmented_any_of(sv.begin(), last, equals_val(80)));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.any.of] mandates "At most last - first applications of the predicate".
|
||||
// The lower bound is not in the standard but follows from the answer: true
|
||||
// cannot be returned without having applied pred to every element up to and
|
||||
// including the first hit, and false cannot be returned without having applied
|
||||
// it to all of them. Bracketing the count catches an element retested when
|
||||
// the scan crosses a segment boundary.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct any_of_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit any_of_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const boost::container::vector<int> flat = test_detail::flatten_n_ints(c, n);
|
||||
std::size_t needed = n;
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
if(pred(flat[i])) { needed = i + 1u; break; }
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_any_of(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_any_of_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, any_of_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, any_of_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_any_of_predicate_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_any_of_count_shapes(vals, n, equals_val((v <= n) ? int(v) : -999));
|
||||
run_any_of_count_shapes(vals, n, multiple_of(1));
|
||||
run_any_of_count_shapes(vals, n, multiple_of(3));
|
||||
run_any_of_count_shapes(vals, n, is_negative());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_any_of_shape_matrix();
|
||||
@@ -285,5 +345,6 @@ int main()
|
||||
test_any_of_single_segment_seg2_single_inner();
|
||||
test_any_of_single_segment_forward();
|
||||
test_any_of_forward_hit_before_last_segment();
|
||||
test_any_of_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -586,6 +586,95 @@ void test_copy_if_unrolled_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.copy] mandates "Exactly last - first applications of the corresponding
|
||||
// predicate", whatever the segmentation of either range. A leaf that applies
|
||||
// pred, then finds the destination segment full and returns without consuming
|
||||
// the element it has just tested hands that element back to the destination
|
||||
// walker, which re-applies pred to it; only an equality catches that.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct copy_if_count_check
|
||||
{
|
||||
template<class CSrc, class CDst>
|
||||
void operator()(CSrc& src, std::size_t n_src, const char* src_spec,
|
||||
CDst& dst, std::size_t n_dst, const char* dst_spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_copy_if(src.begin(), test_detail::iter_at(src, n_src), dst.begin(),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n_src);
|
||||
BOOST_TEST(test_detail::filler_intact(dst, n_dst, shape_filler));
|
||||
BOOST_TEST(src_spec != 0 && dst_spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void copy_if_add_block(test_detail::seg_vector<int>& c, std::size_t block)
|
||||
{ c.add_segment(block, 0); }
|
||||
|
||||
void copy_if_add_block(test_detail::seg2_vector<int>& c, std::size_t block)
|
||||
{
|
||||
test_detail::seg_vector<int> inner;
|
||||
inner.add_segment(block, 0);
|
||||
c.add_segment(inner);
|
||||
}
|
||||
|
||||
// Destination segments of a handful of elements each, so that boundary
|
||||
// crossings dominate: with 64 elements and blocks of 8 there are seven of them,
|
||||
// and the depth-2 destination multiplies them.
|
||||
template<class CDst>
|
||||
void copy_if_count_small_dst(std::size_t n, std::size_t block, bool all_match)
|
||||
{
|
||||
boost::container::vector<int> src;
|
||||
src.reserve(n);
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
src.push_back(all_match ? 2*static_cast<int>(i) + 2 : 2*static_cast<int>(i) + 1);
|
||||
|
||||
CDst dst;
|
||||
for(std::size_t room = 0; room < n; room += block)
|
||||
copy_if_add_block(dst, block);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_copy_if(src.begin(), src.end(), dst.begin(),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
}
|
||||
|
||||
void test_copy_if_predicate_count()
|
||||
{
|
||||
static const int mixed[] = {2, 7, 4, 9, 6, 11, 8, 13, 10, 15};
|
||||
static const int all_hit[] = {2, 4, 6, 8, 10, 12};
|
||||
static const int no_hit[] = {1, 3, 5, 7, 9, 11};
|
||||
static const int* const sets[] = {mixed, all_hit, no_hit};
|
||||
static const std::size_t set_len[] = {10u, 6u, 6u};
|
||||
static const std::size_t sizes[] = {0u, 1u, 2u, 3u, 5u, 6u};
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sets)/sizeof(sets[0]); ++s) {
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
||||
const std::size_t n_src = sizes[i];
|
||||
if(n_src > set_len[s])
|
||||
continue;
|
||||
const std::size_t m = shape_match_count(sets[s], n_src);
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(sets[s], n_src, shape_dst_vals, m, shape_filler, copy_if_count_check());
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(sets[s], n_src, shape_dst_vals, m + 3u, shape_filler, copy_if_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
static const std::size_t blocks[] = {1u, 2u, 3u, 8u, 16u};
|
||||
for(std::size_t b = 0; b != sizeof(blocks)/sizeof(blocks[0]); ++b) {
|
||||
for(int am = 0; am != 2; ++am) {
|
||||
const bool a = am != 0;
|
||||
copy_if_count_small_dst<test_detail::seg_vector<int> >(64u, blocks[b], a);
|
||||
copy_if_count_small_dst<test_detail::seg2_vector<int> >(64u, blocks[b], a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_copy_if_full_range();
|
||||
@@ -610,5 +699,6 @@ int main()
|
||||
test_copy_if_single_segment_dst_from_flat();
|
||||
test_copy_if_shape_matrix();
|
||||
test_copy_if_unrolled_blocks();
|
||||
test_copy_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -295,6 +295,41 @@ void test_count_if_forward_multi_segment()
|
||||
BOOST_TEST_EQ(segmented_count_if(sv.begin(), test_detail::iter_at(sv, 7), is_negative()), 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.count] mandates "Exactly last - first applications of the corresponding
|
||||
// predicate", so a segment walked twice or an element skipped shows up here
|
||||
// even in the cases where the returned total still comes out right.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct count_if_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_count_if(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_count_if_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16] = { 0 };
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
vals[i] = int(i) + 1;
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, count_if_count_check());
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, count_if_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_count_if_shape_matrix();
|
||||
@@ -310,5 +345,6 @@ int main()
|
||||
test_count_if_single_segment_seg2_single_inner();
|
||||
test_count_if_single_segment_forward();
|
||||
test_count_if_forward_multi_segment();
|
||||
test_count_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -257,6 +257,56 @@ void test_count_forward_multi_segment()
|
||||
BOOST_TEST_EQ(segmented_count(sv.begin(), test_detail::iter_at(sv, 7), 99), 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.count] mandates "Exactly last - first applications of the corresponding
|
||||
// predicate", so an element compared twice at a segment boundary is a
|
||||
// conformance failure, not just a slowdown. There is no comparator overload,
|
||||
// so the count is taken from the value type itself.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct count_comparison_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const test_detail::counted_int target(3);
|
||||
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_count(c.begin(), test_detail::iter_at(c, n), target);
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST_EQ(applied, n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_count_comparison_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
// No match, every element a match, and a scattering of matches.
|
||||
for(std::size_t i = 0; i != 16u; ++i)
|
||||
vals[i] = int(i) + 100;
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, count_comparison_check());
|
||||
|
||||
for(std::size_t i = 0; i != 16u; ++i)
|
||||
vals[i] = 3;
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, count_comparison_check());
|
||||
|
||||
for(std::size_t i = 0; i != 16u; ++i)
|
||||
vals[i] = int(i) % 3 == 0 ? 3 : int(i) + 100;
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, count_comparison_check());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_count_shape_matrix();
|
||||
@@ -270,5 +320,6 @@ int main()
|
||||
test_count_single_segment_seg2_single_inner();
|
||||
test_count_single_segment_forward();
|
||||
test_count_forward_multi_segment();
|
||||
test_count_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -607,6 +607,78 @@ void test_equal_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.equal] mandates "At most last1 - first1 applications of the
|
||||
// corresponding predicate". The lower bound below is what stops the check
|
||||
// from passing vacuously: the answer cannot be known before the first
|
||||
// differing position has been looked at.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct eq_int
|
||||
{
|
||||
bool operator()(int a, int b) const { return a == b; }
|
||||
};
|
||||
|
||||
struct equal_count_check
|
||||
{
|
||||
// Index of the element of range 2 that was corrupted, or n1 for none.
|
||||
std::size_t bad_pos;
|
||||
|
||||
explicit equal_count_check(std::size_t p) : bad_pos(p) {}
|
||||
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
const std::size_t needed = bad_pos < n1 ? bad_pos + 1u : n1;
|
||||
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_equal(c1.begin(), test_detail::iter_at(c1, n1), c2.begin(),
|
||||
test_detail::counting_pred(calls, eq_int()));
|
||||
BOOST_TEST(calls.n <= n1);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
}
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_equal(c1.begin(), test_detail::make_sentinel(test_detail::iter_at(c1, n1)),
|
||||
c2.begin(), test_detail::counting_pred(calls, eq_int()));
|
||||
BOOST_TEST(calls.n <= n1);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0 && n2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_equal_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 9u };
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n1 = sizes[s];
|
||||
const std::size_t n2 = n1 + 1u;
|
||||
|
||||
int v1[10] = {};
|
||||
for(std::size_t i = 0; i != n1; ++i)
|
||||
v1[i] = int(i) + 1;
|
||||
|
||||
for(std::size_t bad = 0; bad <= n1; ++bad) {
|
||||
int v2[11] = {};
|
||||
for(std::size_t i = 0; i != n1; ++i)
|
||||
v2[i] = v1[i];
|
||||
v2[n1] = equal_shape_tail;
|
||||
if(bad != n1)
|
||||
v2[bad] = -7;
|
||||
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, n1, v2, n2, -999, equal_count_check(bad));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_equal_shape_matrix();
|
||||
@@ -639,5 +711,6 @@ int main()
|
||||
test_equal_single_segment_flat_first_range();
|
||||
test_equal_single_segment_every_position();
|
||||
|
||||
test_equal_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -125,11 +125,57 @@ void test_fill_n_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Assignment count.
|
||||
//
|
||||
// [alg.fill] mandates "Exactly n assignments", so a slot written twice at a
|
||||
// segment boundary is a conformance failure. The count is taken from the
|
||||
// value type, fill_n having no functor to instrument.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct fill_n_assignment_check
|
||||
{
|
||||
std::size_t offset;
|
||||
std::size_t count;
|
||||
|
||||
fill_n_assignment_check(std::size_t o, std::size_t k) : offset(o), count(k) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const test_detail::counted_int value(42);
|
||||
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_fill_n(test_detail::iter_at(c, offset), count, value);
|
||||
const std::size_t applied = test_detail::counted_int_ops().assign;
|
||||
|
||||
BOOST_TEST_EQ(applied, count);
|
||||
BOOST_TEST(spec != 0 && n >= count);
|
||||
}
|
||||
};
|
||||
|
||||
void test_fill_n_assignment_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t offset = 0; offset <= n; ++offset)
|
||||
for(std::size_t k = 0; offset + k <= n; ++k)
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, fill_n_assignment_check(offset, k));
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_fill_n_shape_matrix();
|
||||
test_fill_n_partial();
|
||||
test_fill_n_single_segment();
|
||||
test_fill_n_non_segmented();
|
||||
test_fill_n_assignment_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -170,6 +170,42 @@ void test_fill_single_segment_sentinel()
|
||||
BOOST_TEST_EQ(*it, expected[i]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Assignment count.
|
||||
//
|
||||
// [alg.fill] mandates "Exactly last - first assignments", so a slot written
|
||||
// twice at a segment boundary is a conformance failure. The count is taken
|
||||
// from the value type, fill having no functor to instrument.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct fill_assignment_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const test_detail::counted_int value(42);
|
||||
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_fill(c.begin(), test_detail::iter_at(c, n), value);
|
||||
const std::size_t applied = test_detail::counted_int_ops().assign;
|
||||
|
||||
BOOST_TEST_EQ(applied, n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_fill_assignment_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s)
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, sizes[s], -999, fill_assignment_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_fill_shape_matrix();
|
||||
@@ -181,5 +217,6 @@ int main()
|
||||
test_fill_sentinel_non_segmented();
|
||||
test_fill_seg2();
|
||||
test_fill_single_segment_sentinel();
|
||||
test_fill_assignment_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -334,6 +334,66 @@ void test_find_if_not_forward_match_before_last_segment()
|
||||
BOOST_TEST(segmented_find_if_not(sv.begin(), last, not_equals_val(80)) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.find] mandates "At most last - first applications of the corresponding
|
||||
// predicate". The lower bound is not in the standard but follows from the
|
||||
// answer: the result cannot be known without having applied pred to every
|
||||
// element up to and including the one returned. Bracketing the count between
|
||||
// the two catches an element retested when the scan crosses a segment
|
||||
// boundary as well as a scan that stops short of the answer.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct find_if_not_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit find_if_not_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const boost::container::vector<int> flat = test_detail::flatten_n_ints(c, n);
|
||||
std::size_t needed = n;
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
if(!pred(flat[i])) { needed = i + 1u; break; }
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_find_if_not(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_find_if_not_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, find_if_not_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, find_if_not_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_find_if_not_predicate_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_find_if_not_count_shapes(vals, n, not_equals_val((v <= n) ? int(v) : -999));
|
||||
run_find_if_not_count_shapes(vals, n, not_multiple_of(1));
|
||||
run_find_if_not_count_shapes(vals, n, not_multiple_of(3));
|
||||
run_find_if_not_count_shapes(vals, n, is_positive());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_find_if_not_shape_matrix();
|
||||
@@ -349,5 +409,6 @@ int main()
|
||||
test_find_if_not_single_segment_seg2_single_inner();
|
||||
test_find_if_not_single_segment_forward();
|
||||
test_find_if_not_forward_match_before_last_segment();
|
||||
test_find_if_not_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -331,6 +331,66 @@ void test_find_if_forward_match_before_last_segment()
|
||||
BOOST_TEST(segmented_find_if(sv.begin(), last, equals_val(80)) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.find] mandates "At most last - first applications of the corresponding
|
||||
// predicate". The lower bound is not in the standard but follows from the
|
||||
// answer: the result cannot be known without having applied pred to every
|
||||
// element up to and including the one returned. Bracketing the count between
|
||||
// the two catches an element retested when the scan crosses a segment
|
||||
// boundary as well as a scan that stops short of the answer.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct find_if_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit find_if_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const boost::container::vector<int> flat = test_detail::flatten_n_ints(c, n);
|
||||
std::size_t needed = n;
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
if(pred(flat[i])) { needed = i + 1u; break; }
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_find_if(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_find_if_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, find_if_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, find_if_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_find_if_predicate_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_find_if_count_shapes(vals, n, equals_val((v <= n) ? int(v) : -999));
|
||||
run_find_if_count_shapes(vals, n, multiple_of(1));
|
||||
run_find_if_count_shapes(vals, n, multiple_of(3));
|
||||
run_find_if_count_shapes(vals, n, is_negative());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_find_if_shape_matrix();
|
||||
@@ -346,5 +406,6 @@ int main()
|
||||
test_find_if_single_segment_seg2_single_inner();
|
||||
test_find_if_single_segment_forward();
|
||||
test_find_if_forward_match_before_last_segment();
|
||||
test_find_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -310,6 +310,65 @@ void test_find_last_if_not_forward_match_in_earlier_segment_seg2()
|
||||
BOOST_TEST(segmented_find_last_if_not(sv2.begin(), last, not_equals_val(6)) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.find.last] mandates "At most last - first applications of the
|
||||
// corresponding predicate", so neither the forward implementation, which has
|
||||
// to walk the whole range, nor the bidirectional one, which scans backwards
|
||||
// from the end, may retest an element at a segment boundary.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct find_last_if_not_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit find_last_if_not_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_find_last_if_not(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_find_last_if_not_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, find_last_if_not_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, find_last_if_not_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_find_last_if_not_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i/2 + 1;
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_find_last_if_not_count_shapes(vals, n, not_equals_val((v <= n) ? int(v) : -999));
|
||||
|
||||
for(std::size_t p = 0; p != n; ++p) {
|
||||
for(std::size_t i = 0; i != 16u; ++i)
|
||||
vals[i] = (i == p) ? -int(i + 1u) : int(i + 1u);
|
||||
run_find_last_if_not_count_shapes(vals, n, is_positive());
|
||||
}
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
run_find_last_if_not_count_shapes(vals, n, is_positive());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_find_last_if_not_shape_matrix();
|
||||
@@ -325,5 +384,6 @@ int main()
|
||||
test_find_last_if_not_single_segment_sentinel();
|
||||
test_find_last_if_not_forward_match_in_earlier_segment();
|
||||
test_find_last_if_not_forward_match_in_earlier_segment_seg2();
|
||||
test_find_last_if_not_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -310,6 +310,65 @@ void test_find_last_if_forward_match_in_earlier_segment_seg2()
|
||||
BOOST_TEST(segmented_find_last_if(sv2.begin(), last, equals_val(6)) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.find.last] mandates "At most last - first applications of the
|
||||
// corresponding predicate", so neither the forward implementation, which has
|
||||
// to walk the whole range, nor the bidirectional one, which scans backwards
|
||||
// from the end, may retest an element at a segment boundary.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct find_last_if_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit find_last_if_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_find_last_if(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_find_last_if_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, find_last_if_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, find_last_if_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_find_last_if_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i/2 + 1;
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_find_last_if_count_shapes(vals, n, equals_val((v <= n) ? int(v) : -999));
|
||||
|
||||
for(std::size_t p = 0; p != n; ++p) {
|
||||
for(std::size_t i = 0; i != 16u; ++i)
|
||||
vals[i] = (i == p) ? -int(i + 1u) : int(i + 1u);
|
||||
run_find_last_if_count_shapes(vals, n, is_negative());
|
||||
}
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
run_find_last_if_count_shapes(vals, n, is_negative());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_find_last_if_shape_matrix();
|
||||
@@ -325,5 +384,6 @@ int main()
|
||||
test_find_last_if_single_segment_sentinel();
|
||||
test_find_last_if_forward_match_in_earlier_segment();
|
||||
test_find_last_if_forward_match_in_earlier_segment_seg2();
|
||||
test_find_last_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -277,6 +277,53 @@ void test_find_last_forward_match_in_earlier_segment_seg2()
|
||||
BOOST_TEST(segmented_find_last(sv2.begin(), last, 6) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.find.last] mandates "At most last - first applications of the
|
||||
// corresponding predicate and projection", which holds for the backward scan
|
||||
// a bidirectional range allows as much as for the forward one a forward range
|
||||
// forces. There is no predicate overload, so the count comes from the value
|
||||
// type.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct find_last_comparison_check
|
||||
{
|
||||
int value;
|
||||
|
||||
explicit find_last_comparison_check(int v) : value(v) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_find_last(c.begin(), test_detail::iter_at(c, n), test_detail::counted_int(value));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST(applied <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_find_last_comparison_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i/2 + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v) {
|
||||
const int target = (v <= n) ? int(v) : -999;
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, find_last_comparison_check(target));
|
||||
test_detail::for_each_shape_all_fwd<test_detail::counted_int>
|
||||
(vals, n, -999, find_last_comparison_check(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_find_last_shape_matrix();
|
||||
@@ -292,5 +339,6 @@ int main()
|
||||
test_find_last_single_segment_sentinel();
|
||||
test_find_last_forward_match_in_earlier_segment();
|
||||
test_find_last_forward_match_in_earlier_segment_seg2();
|
||||
test_find_last_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -406,6 +406,57 @@ void test_find_forward_match_before_last_segment()
|
||||
BOOST_TEST(segmented_find(sv.begin(), last, 80) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.find] mandates "At most last - first applications of the corresponding
|
||||
// predicate". The lower bound is what stops the check from passing vacuously:
|
||||
// the answer cannot be known before the element at it has been compared.
|
||||
// There is no predicate overload, so the count comes from the value type.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct find_comparison_check
|
||||
{
|
||||
int value;
|
||||
|
||||
explicit find_comparison_check(int v) : value(v) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const boost::container::vector<int> flat = test_detail::flatten_n_ints(c, n);
|
||||
std::size_t expected = flat.size();
|
||||
for(std::size_t i = 0; i != flat.size(); ++i) {
|
||||
if(flat[i] == value) { expected = i; break; }
|
||||
}
|
||||
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_find(c.begin(), test_detail::iter_at(c, n), test_detail::counted_int(value));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST(applied <= n);
|
||||
BOOST_TEST(applied >= (expected < n ? expected + 1u : n));
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_find_comparison_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v) {
|
||||
const int target = (v <= n) ? int(v) : -999;
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, find_comparison_check(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_find_shape_matrix();
|
||||
@@ -427,5 +478,6 @@ int main()
|
||||
test_find_single_segment_seg2_single_inner();
|
||||
test_find_single_segment_forward();
|
||||
test_find_forward_match_before_last_segment();
|
||||
test_find_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -176,6 +176,40 @@ void test_for_each_single_segment_sentinel()
|
||||
BOOST_TEST_EQ(*it, expected[i]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Function application count.
|
||||
//
|
||||
// [alg.foreach] mandates "Applies f exactly last - first times", so a segment
|
||||
// walked twice or one skipped is caught here even in the cases where the
|
||||
// accumulated answer still comes out right.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct for_each_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_for_each(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_fun<doubler, void>(calls, doubler()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
BOOST_TEST(test_detail::filler_intact(c, n, -999));
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_for_each_application_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s)
|
||||
test_detail::for_each_shape_all<int>(vals, sizes[s], -999, for_each_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_for_each_shape_matrix();
|
||||
@@ -186,5 +220,6 @@ int main()
|
||||
test_for_each_sentinel_non_segmented();
|
||||
test_for_each_seg2();
|
||||
test_for_each_single_segment_sentinel();
|
||||
test_for_each_application_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -304,6 +304,66 @@ void test_is_partitioned_forward_violation_before_last_segment()
|
||||
BOOST_TEST(!segmented_is_partitioned(sv.begin(), test_detail::iter_at(sv, 7), less_than_5()));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.partitions] mandates "At most last - first applications of pred" for
|
||||
// is_partitioned, so the two scans it is specified as -- the satisfying prefix
|
||||
// and the non-satisfying remainder -- must between them touch each element at
|
||||
// most once, whatever segment boundary they meet in between.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct is_partitioned_count_check
|
||||
{
|
||||
int threshold;
|
||||
|
||||
explicit is_partitioned_count_check(int t) : threshold(t) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_is_partitioned(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, less_than_threshold(threshold)));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void run_is_partitioned_count_shapes(const int* vals, std::size_t n, int threshold)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, is_partitioned_count_check(threshold));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, is_partitioned_count_check(threshold));
|
||||
}
|
||||
|
||||
void test_is_partitioned_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
std::size_t i = 0;
|
||||
int vals[16] = { 0 };
|
||||
|
||||
// Properly partitioned with the boundary at every position in turn, so
|
||||
// the second scan starts in the middle of a segment as well as at one of
|
||||
// its ends.
|
||||
for(std::size_t p = 0; p <= n; ++p) {
|
||||
for(i = 0; i != n; ++i)
|
||||
vals[i] = (i < p) ? int(i) : 1000 + int(i);
|
||||
run_is_partitioned_count_shapes(vals, n, 500);
|
||||
}
|
||||
|
||||
// Not partitioned: a satisfying element after a non-satisfying one, at
|
||||
// every position in turn, so the early exit lands everywhere too.
|
||||
for(std::size_t p = 1; p < n; ++p) {
|
||||
for(i = 0; i != n; ++i)
|
||||
vals[i] = (i == p) ? int(i) : 1000 + int(i);
|
||||
run_is_partitioned_count_shapes(vals, n, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_is_partitioned_shape_matrix();
|
||||
@@ -319,5 +379,6 @@ int main()
|
||||
test_is_partitioned_single_segment_seg2_single_inner();
|
||||
test_is_partitioned_single_segment_forward();
|
||||
test_is_partitioned_forward_violation_before_last_segment();
|
||||
test_is_partitioned_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -211,6 +211,53 @@ void test_is_sorted_single_segment_sentinel()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.sorted] states the complexity of is_sorted/is_sorted_until as "Linear",
|
||||
// so what is pinned here is the constant: a scan comparing each adjacent pair
|
||||
// once needs last - first - 1 comparisons, and anything above last - first
|
||||
// means a pair compared twice, which is what a segment boundary invites.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct is_sorted_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_is_sorted(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, greater_comp()));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_is_sorted_comparison_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
//Descending, so the range is sorted under greater_comp and the scan has
|
||||
//to run to the end.
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (16 - i) * 10;
|
||||
test_detail::for_each_shape_all<int>(vals, n, 99999, is_sorted_count_check());
|
||||
|
||||
//One inversion at each adjacent pair in turn, so the early exit lands at
|
||||
//every position, inside a segment and at a boundary.
|
||||
for(std::size_t p = 1u; p < n; ++p) {
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (16 - i) * 10;
|
||||
vals[p] = vals[p - 1u] + 1;
|
||||
test_detail::for_each_shape_all<int>(vals, n, 99999, is_sorted_count_check());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_is_sorted_shape_matrix();
|
||||
@@ -222,5 +269,6 @@ int main()
|
||||
test_is_sorted_sentinel_non_segmented();
|
||||
test_is_sorted_seg2();
|
||||
test_is_sorted_single_segment_sentinel();
|
||||
test_is_sorted_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -236,6 +236,53 @@ void test_is_sorted_until_single_segment_sentinel()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.sorted] states the complexity of is_sorted_until as "Linear", so what
|
||||
// is pinned here is the constant: a scan comparing each adjacent pair once
|
||||
// needs last - first - 1 comparisons, and anything above last - first means a
|
||||
// pair compared twice, which is what a segment boundary invites.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct is_sorted_until_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_is_sorted_until(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, greater_comp()));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_is_sorted_until_comparison_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
//Descending, so the range is sorted under greater_comp and the scan has
|
||||
//to run to the end.
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (16 - i) * 10;
|
||||
test_detail::for_each_shape_all<int>(vals, n, 99999, is_sorted_until_count_check());
|
||||
|
||||
//One inversion at each adjacent pair in turn, so the early exit lands at
|
||||
//every position, inside a segment and at a boundary.
|
||||
for(std::size_t p = 1u; p < n; ++p) {
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (16 - i) * 10;
|
||||
vals[p] = vals[p - 1u] + 1;
|
||||
test_detail::for_each_shape_all<int>(vals, n, 99999, is_sorted_until_count_check());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_is_sorted_until_shape_matrix();
|
||||
@@ -246,5 +293,6 @@ int main()
|
||||
test_is_sorted_until_every_position();
|
||||
test_is_sorted_until_every_position_seg2();
|
||||
test_is_sorted_until_single_segment_sentinel();
|
||||
test_is_sorted_until_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -990,6 +990,69 @@ void test_merge_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.merge] mandates "At most N - 1 comparisons", N being the total input
|
||||
// length. That is far tighter than the set operations allow, so a single
|
||||
// element re-compared at a destination boundary already breaks it.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct less_comp
|
||||
{
|
||||
bool operator()(int a, int b) const { return a < b; }
|
||||
};
|
||||
|
||||
inline std::size_t merge_comparison_bound(std::size_t n1, std::size_t n2)
|
||||
{
|
||||
const std::size_t total = n1 + n2;
|
||||
return total ? total - 1u : 0u;
|
||||
}
|
||||
|
||||
struct merge_count_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
boost::container::vector<int> flat(n1 + n2 + 1u, -1);
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_merge(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
flat.begin(), test_detail::counting_pred(calls, less_comp()));
|
||||
BOOST_TEST(calls.n <= merge_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
for(std::size_t block = 1u; block <= 3u; ++block) {
|
||||
test_detail::seg_vector<int> out;
|
||||
for(std::size_t room = 0; room <= n1 + n2; room += block)
|
||||
out.add_segment(block, -1);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_merge(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
out.begin(), test_detail::counting_pred(calls, less_comp()));
|
||||
BOOST_TEST(calls.n <= merge_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_merge_comparison_count()
|
||||
{
|
||||
const int v1[] = {1, 2, 2, 3, 5, 8};
|
||||
const int v2[] = {2, 3, 3, 4, 8, 9};
|
||||
|
||||
static const std::size_t pairs[][2] =
|
||||
{ {0u, 0u}, {0u, 3u}, {3u, 0u}, {1u, 1u}, {2u, 4u}, {4u, 2u}, {5u, 6u} };
|
||||
|
||||
for(std::size_t p = 0; p != sizeof(pairs)/sizeof(pairs[0]); ++p)
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, pairs[p][0], v2, pairs[p][1], -999, merge_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_merge_shape_matrix();
|
||||
@@ -1031,5 +1094,6 @@ int main()
|
||||
test_merge_single_segment_sentinel();
|
||||
test_merge_single_segment_with_comp();
|
||||
|
||||
test_merge_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -1393,6 +1393,92 @@ void test_mismatch_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [mismatch] mandates "At most last1 - first1 applications of the
|
||||
// corresponding predicate", and min(last1 - first1, last2 - first2) for the
|
||||
// four-iterator form. The lower bound below is what stops the check from
|
||||
// passing vacuously: the returned position cannot be known before the element
|
||||
// at it has been looked at.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct mismatch_eq_int
|
||||
{
|
||||
bool operator()(int a, int b) const { return a == b; }
|
||||
};
|
||||
|
||||
struct mismatch_count_check
|
||||
{
|
||||
// Index of the element of range 2 that was corrupted, or n1 for none.
|
||||
std::size_t bad_pos;
|
||||
|
||||
explicit mismatch_count_check(std::size_t p) : bad_pos(p) {}
|
||||
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
const std::size_t k = bad_pos < n1 ? bad_pos : n1;
|
||||
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_mismatch(c1.begin(), test_detail::iter_at(c1, n1), c2.begin(),
|
||||
test_detail::counting_pred(calls, mismatch_eq_int()));
|
||||
BOOST_TEST(calls.n <= n1);
|
||||
BOOST_TEST(calls.n >= (k < n1 ? k + 1u : n1));
|
||||
}
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_mismatch(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
test_detail::counting_pred(calls, mismatch_eq_int()));
|
||||
BOOST_TEST(calls.n <= (n1 < n2 ? n1 : n2));
|
||||
BOOST_TEST(calls.n >= (k < n1 ? k + 1u : n1));
|
||||
}
|
||||
{
|
||||
// Second range bounded short, so the walk can also stop on it.
|
||||
const std::size_t half = n1 / 2u;
|
||||
const std::size_t k_half = k < half ? k : half;
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_mismatch(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, half),
|
||||
test_detail::counting_pred(calls, mismatch_eq_int()));
|
||||
BOOST_TEST(calls.n <= half);
|
||||
BOOST_TEST(calls.n >= (k_half < half ? k_half + 1u : half));
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_mismatch_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 9u };
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n1 = sizes[s];
|
||||
const std::size_t n2 = n1 + 1u;
|
||||
|
||||
int v1[10] = {};
|
||||
for(std::size_t i = 0; i != n1; ++i)
|
||||
v1[i] = int(i) + 1;
|
||||
|
||||
for(std::size_t bad = 0; bad <= n1; ++bad) {
|
||||
int v2[11] = {};
|
||||
for(std::size_t i = 0; i != n1; ++i)
|
||||
v2[i] = v1[i];
|
||||
v2[n1] = mismatch_shape_tail;
|
||||
if(bad != n1)
|
||||
v2[bad] = -7;
|
||||
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, n1, v2, n2, -999, mismatch_count_check(bad));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_mismatch_shape_matrix();
|
||||
@@ -1456,5 +1542,6 @@ int main()
|
||||
test_mismatch_2r_single_segment_second_range_seg2();
|
||||
test_mismatch_2r_single_segment_every_position();
|
||||
|
||||
test_mismatch_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -272,6 +272,66 @@ void test_none_of_forward_hit_before_last_segment()
|
||||
BOOST_TEST(segmented_none_of(sv.begin(), last, equals_val(80)));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.none.of] mandates "At most last - first applications of the predicate".
|
||||
// The lower bound is not in the standard but follows from the answer: false
|
||||
// cannot be returned without having applied pred to every element up to and
|
||||
// including the first hit, and true cannot be returned without having applied
|
||||
// it to all of them. Bracketing the count catches an element retested when
|
||||
// the scan crosses a segment boundary.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct none_of_count_check
|
||||
{
|
||||
Pred pred;
|
||||
|
||||
explicit none_of_count_check(Pred p) : pred(p) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
const boost::container::vector<int> flat = test_detail::flatten_n_ints(c, n);
|
||||
std::size_t needed = n;
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
if(pred(flat[i])) { needed = i + 1u; break; }
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_none_of(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, pred));
|
||||
|
||||
BOOST_TEST(calls.n <= n);
|
||||
BOOST_TEST(calls.n >= needed);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Pred>
|
||||
void run_none_of_count_shapes(const int* vals, std::size_t n, Pred pred)
|
||||
{
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, none_of_count_check<Pred>(pred));
|
||||
test_detail::for_each_shape_all_fwd<int>(vals, n, -999, none_of_count_check<Pred>(pred));
|
||||
}
|
||||
|
||||
void test_none_of_predicate_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n + 1u; ++v)
|
||||
run_none_of_count_shapes(vals, n, equals_val((v <= n) ? int(v) : -999));
|
||||
run_none_of_count_shapes(vals, n, multiple_of(1));
|
||||
run_none_of_count_shapes(vals, n, multiple_of(3));
|
||||
run_none_of_count_shapes(vals, n, is_negative());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_none_of_shape_matrix();
|
||||
@@ -285,5 +345,6 @@ int main()
|
||||
test_none_of_single_segment_seg2_single_inner();
|
||||
test_none_of_single_segment_forward();
|
||||
test_none_of_forward_hit_before_last_segment();
|
||||
test_none_of_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -630,6 +630,99 @@ void test_partition_copy_unrolled_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.partitions] mandates "Exactly last - first applications of pred" for
|
||||
// partition_copy, whatever the segmentation of the source and of either
|
||||
// output. A leaf that applies pred, then finds one of the two destination
|
||||
// segments full and returns without consuming the element it has just tested
|
||||
// hands that element back to the walker, which re-applies pred to it.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct partition_copy_count_check
|
||||
{
|
||||
template<class CSrc, class CTrue, class CFalse>
|
||||
void operator()(CSrc& src, std::size_t n_src, const char* src_spec,
|
||||
CTrue& tdst, std::size_t n_t, const char* t_spec,
|
||||
CFalse& fdst, std::size_t n_f, const char* f_spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_partition_copy(src.begin(), test_detail::iter_at(src, n_src),
|
||||
tdst.begin(), fdst.begin(),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n_src);
|
||||
BOOST_TEST(test_detail::filler_intact(tdst, n_t, shape_filler));
|
||||
BOOST_TEST(test_detail::filler_intact(fdst, n_f, shape_filler));
|
||||
BOOST_TEST(src_spec != 0 && t_spec != 0 && f_spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void partition_copy_add_block(test_detail::seg_vector<int>& c, std::size_t block)
|
||||
{ c.add_segment(block, 0); }
|
||||
|
||||
void partition_copy_add_block(test_detail::seg2_vector<int>& c, std::size_t block)
|
||||
{
|
||||
test_detail::seg_vector<int> inner;
|
||||
inner.add_segment(block, 0);
|
||||
c.add_segment(inner);
|
||||
}
|
||||
|
||||
// Output segments of a handful of elements each, so that boundary crossings
|
||||
// dominate: both outputs cross, and the depth-2 form multiplies the crossings.
|
||||
template<class CDst>
|
||||
void partition_copy_count_small_dst(std::size_t n, std::size_t block)
|
||||
{
|
||||
boost::container::vector<int> src;
|
||||
src.reserve(n);
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
src.push_back(static_cast<int>(i) + 1);
|
||||
|
||||
CDst tdst, fdst;
|
||||
for(std::size_t room = 0; room < n; room += block) {
|
||||
partition_copy_add_block(tdst, block);
|
||||
partition_copy_add_block(fdst, block);
|
||||
}
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_partition_copy(src.begin(), src.end(), tdst.begin(), fdst.begin(),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
}
|
||||
|
||||
void test_partition_copy_predicate_count()
|
||||
{
|
||||
static const int mixed[] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
static const int t_only[] = {2, 4, 6, 8, 10, 12};
|
||||
static const int f_only[] = {1, 3, 5, 7, 9, 11};
|
||||
static const int* const sets[] = {mixed, t_only, f_only};
|
||||
static const std::size_t set_len[] = {8u, 6u, 6u};
|
||||
static const std::size_t sizes[] = {0u, 1u, 2u, 5u};
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sets)/sizeof(sets[0]); ++s) {
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
||||
const std::size_t n = sizes[i];
|
||||
if(n > set_len[s])
|
||||
continue;
|
||||
const std::size_t nt = shape_true_count(sets[s], n);
|
||||
const std::size_t nf = n - nt;
|
||||
test_detail::for_each_shape3_all<int, int, int>
|
||||
(sets[s], n, shape_dst_vals, nt, shape_dst_vals, nf,
|
||||
shape_filler, partition_copy_count_check());
|
||||
test_detail::for_each_shape3_all<int, int, int>
|
||||
(sets[s], n, shape_dst_vals, nt + 2u, shape_dst_vals, nf + 2u,
|
||||
shape_filler, partition_copy_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
static const std::size_t blocks[] = {1u, 2u, 3u, 8u, 16u};
|
||||
for(std::size_t b = 0; b != sizeof(blocks)/sizeof(blocks[0]); ++b) {
|
||||
partition_copy_count_small_dst<test_detail::seg_vector<int> >(64u, blocks[b]);
|
||||
partition_copy_count_small_dst<test_detail::seg2_vector<int> >(64u, blocks[b]);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_partition_copy_segmented();
|
||||
@@ -651,5 +744,6 @@ int main()
|
||||
test_partition_copy_single_segment_outs_from_flat();
|
||||
test_partition_copy_shape_matrix();
|
||||
test_partition_copy_unrolled_blocks();
|
||||
test_partition_copy_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -343,6 +343,54 @@ void test_partition_forward_matches_in_earlier_segment()
|
||||
BOOST_TEST_EQ(*test_detail::iter_at(sv, 6), 99);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.partitions] mandates that partition performs "Exactly N applications of
|
||||
// the predicate", N = last - first, whichever iterator category is used, so
|
||||
// neither the two-ended bidirectional scan nor the forward one may retest an
|
||||
// element when it crosses a segment boundary.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct partition_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_partition(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
BOOST_TEST(test_detail::filler_intact(c, n, -999));
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_partition_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
run_partition_shapes(vals, n, partition_count_check());
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = 2*i + 2;
|
||||
run_partition_shapes(vals, n, partition_count_check());
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = 2*i + 1;
|
||||
run_partition_shapes(vals, n, partition_count_check());
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (i < 8) ? 2*i + 1 : 2*i + 2;
|
||||
run_partition_shapes(vals, n, partition_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_partition_shape_matrix();
|
||||
@@ -356,5 +404,6 @@ int main()
|
||||
test_partition_movable_seg2();
|
||||
test_partition_single_segment_sentinel();
|
||||
test_partition_forward_matches_in_earlier_segment();
|
||||
test_partition_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -492,6 +492,95 @@ void test_remove_copy_if_unrolled_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.remove] mandates "Exactly last - first applications of the corresponding
|
||||
// predicate", whatever the segmentation of either range. A leaf that applies
|
||||
// pred, then finds the destination segment full and returns without consuming
|
||||
// the element it has just tested hands that element back to the destination
|
||||
// walker, which re-applies pred to it; only an equality catches that.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct remove_copy_if_count_check
|
||||
{
|
||||
template<class CSrc, class CDst>
|
||||
void operator()(CSrc& src, std::size_t n_src, const char* src_spec,
|
||||
CDst& dst, std::size_t n_dst, const char* dst_spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_remove_copy_if(src.begin(), test_detail::iter_at(src, n_src), dst.begin(),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n_src);
|
||||
BOOST_TEST(test_detail::filler_intact(dst, n_dst, shape_filler));
|
||||
BOOST_TEST(src_spec != 0 && dst_spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void remove_copy_if_add_block(test_detail::seg_vector<int>& c, std::size_t block)
|
||||
{ c.add_segment(block, 0); }
|
||||
|
||||
void remove_copy_if_add_block(test_detail::seg2_vector<int>& c, std::size_t block)
|
||||
{
|
||||
test_detail::seg_vector<int> inner;
|
||||
inner.add_segment(block, 0);
|
||||
c.add_segment(inner);
|
||||
}
|
||||
|
||||
// Destination segments of a handful of elements each, so that boundary
|
||||
// crossings dominate: with 64 elements and blocks of 8 there are seven of them,
|
||||
// and the depth-2 destination multiplies them.
|
||||
template<class CDst>
|
||||
void remove_copy_if_count_small_dst(std::size_t n, std::size_t block, bool keep_all)
|
||||
{
|
||||
boost::container::vector<int> src;
|
||||
src.reserve(n);
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
src.push_back(keep_all ? 2*static_cast<int>(i) + 1 : 2*static_cast<int>(i) + 2);
|
||||
|
||||
CDst dst;
|
||||
for(std::size_t room = 0; room < n; room += block)
|
||||
remove_copy_if_add_block(dst, block);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_remove_copy_if(src.begin(), src.end(), dst.begin(),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
}
|
||||
|
||||
void test_remove_copy_if_predicate_count()
|
||||
{
|
||||
static const int mixed[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
static const int keep[] = {1, 3, 5, 7, 9, 11};
|
||||
static const int drop[] = {2, 4, 6, 8, 10, 12};
|
||||
static const int* const sets[] = {mixed, keep, drop};
|
||||
static const std::size_t set_len[] = {10u, 6u, 6u};
|
||||
static const std::size_t sizes[] = {0u, 1u, 2u, 3u, 5u, 6u};
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sets)/sizeof(sets[0]); ++s) {
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
||||
const std::size_t n_src = sizes[i];
|
||||
if(n_src > set_len[s])
|
||||
continue;
|
||||
const std::size_t m = shape_survivor_count(sets[s], n_src);
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(sets[s], n_src, shape_dst_vals, m, shape_filler, remove_copy_if_count_check());
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(sets[s], n_src, shape_dst_vals, m + 3u, shape_filler, remove_copy_if_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
static const std::size_t blocks[] = {1u, 2u, 3u, 8u, 16u};
|
||||
for(std::size_t b = 0; b != sizeof(blocks)/sizeof(blocks[0]); ++b) {
|
||||
for(int ka = 0; ka != 2; ++ka) {
|
||||
const bool k = ka != 0;
|
||||
remove_copy_if_count_small_dst<test_detail::seg_vector<int> >(64u, blocks[b], k);
|
||||
remove_copy_if_count_small_dst<test_detail::seg2_vector<int> >(64u, blocks[b], k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_remove_copy_if_segmented();
|
||||
@@ -514,5 +603,6 @@ int main()
|
||||
test_remove_copy_if_single_segment_dst_from_flat();
|
||||
test_remove_copy_if_shape_matrix();
|
||||
test_remove_copy_if_unrolled_blocks();
|
||||
test_remove_copy_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -463,6 +463,100 @@ void test_remove_copy_unrolled_blocks()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.remove] mandates "Exactly last - first applications of the
|
||||
// corresponding predicate or comparison", whatever the segmentation of either
|
||||
// range. A leaf that compares an element, then finds the destination segment
|
||||
// full and returns without consuming it hands that element back to the
|
||||
// destination walker, which compares it again; only an equality catches that.
|
||||
// There is no predicate overload, so the count comes from the value type.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct remove_copy_count_check
|
||||
{
|
||||
template<class CSrc, class CDst>
|
||||
void operator()(CSrc& src, std::size_t n_src, const char* src_spec,
|
||||
CDst& dst, std::size_t n_dst, const char* dst_spec) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_remove_copy(src.begin(), test_detail::iter_at(src, n_src), dst.begin(),
|
||||
test_detail::counted_int(shape_removed));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST_EQ(applied, n_src);
|
||||
BOOST_TEST(src_spec != 0 && dst_spec != 0 && n_dst <= n_src + 3u);
|
||||
}
|
||||
};
|
||||
|
||||
void remove_copy_add_block(test_detail::seg_vector<test_detail::counted_int>& c, std::size_t block)
|
||||
{ c.add_segment(block, test_detail::counted_int(0)); }
|
||||
|
||||
void remove_copy_add_block(test_detail::seg2_vector<test_detail::counted_int>& c, std::size_t block)
|
||||
{
|
||||
test_detail::seg_vector<test_detail::counted_int> inner;
|
||||
inner.add_segment(block, test_detail::counted_int(0));
|
||||
c.add_segment(inner);
|
||||
}
|
||||
|
||||
// Destination segments of a handful of elements each, so that boundary
|
||||
// crossings dominate: with 64 elements and blocks of 8 there are seven of them,
|
||||
// and the depth-2 destination multiplies them.
|
||||
template<class CDst>
|
||||
void remove_copy_count_small_dst(std::size_t n, std::size_t block, bool all_kept)
|
||||
{
|
||||
boost::container::vector<test_detail::counted_int> src;
|
||||
src.reserve(n);
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
src.push_back(test_detail::counted_int
|
||||
(all_kept ? static_cast<int>(i) + 10 : shape_removed));
|
||||
|
||||
CDst dst;
|
||||
for(std::size_t room = 0; room < n; room += block)
|
||||
remove_copy_add_block(dst, block);
|
||||
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_remove_copy(src.begin(), src.end(), dst.begin(),
|
||||
test_detail::counted_int(shape_removed));
|
||||
BOOST_TEST_EQ(test_detail::counted_int_ops().cmp, n);
|
||||
}
|
||||
|
||||
void test_remove_copy_comparison_count()
|
||||
{
|
||||
static const int mixed[] = {1, 2, 3, 2, 4, 2, 5, 6, 2, 7};
|
||||
static const int edges[] = {2, 1, 3, 4, 5, 2};
|
||||
static const int none[] = {1, 3, 5, 7, 9, 11};
|
||||
static const int all_rm[] = {2, 2, 2, 2, 2, 2};
|
||||
static const int* const sets[] = {mixed, edges, none, all_rm};
|
||||
static const std::size_t set_len[] = {10u, 6u, 6u, 6u};
|
||||
static const std::size_t sizes[] = {0u, 1u, 2u, 3u, 5u, 6u};
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sets)/sizeof(sets[0]); ++s) {
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
||||
const std::size_t n_src = sizes[i];
|
||||
if(n_src > set_len[s])
|
||||
continue;
|
||||
const std::size_t m = shape_survivor_count(sets[s], n_src);
|
||||
test_detail::for_each_shape2_all<test_detail::counted_int, test_detail::counted_int>
|
||||
(sets[s], n_src, shape_dst_vals, m, shape_filler, remove_copy_count_check());
|
||||
test_detail::for_each_shape2_all<test_detail::counted_int, test_detail::counted_int>
|
||||
(sets[s], n_src, shape_dst_vals, m + 3u, shape_filler, remove_copy_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
static const std::size_t blocks[] = {1u, 2u, 3u, 8u, 16u};
|
||||
for(std::size_t b = 0; b != sizeof(blocks)/sizeof(blocks[0]); ++b) {
|
||||
for(int ak = 0; ak != 2; ++ak) {
|
||||
const bool a = ak != 0;
|
||||
remove_copy_count_small_dst<test_detail::seg_vector<test_detail::counted_int> >
|
||||
(64u, blocks[b], a);
|
||||
remove_copy_count_small_dst<test_detail::seg2_vector<test_detail::counted_int> >
|
||||
(64u, blocks[b], a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_remove_copy_segmented();
|
||||
@@ -483,5 +577,6 @@ int main()
|
||||
test_remove_copy_single_segment_dst_from_flat();
|
||||
test_remove_copy_shape_matrix();
|
||||
test_remove_copy_unrolled_blocks();
|
||||
test_remove_copy_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -406,6 +406,55 @@ void test_remove_if_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.remove] mandates "Exactly last - first applications of the corresponding
|
||||
// predicate". segmented_remove_if reaches that total in two pieces, the
|
||||
// find_if that locates the first match and the compacting pass over the rest,
|
||||
// so an element tested by both, or one re-tested when the write pointer crosses
|
||||
// a segment boundary, shows up as a surplus here.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class Pred>
|
||||
struct remove_if_count_check
|
||||
{
|
||||
template<class C>
|
||||
void operator()(C& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_remove_if(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, Pred()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
BOOST_TEST(test_detail::filler_intact(c, n, shape_filler));
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_remove_if_predicate_count()
|
||||
{
|
||||
static const int mixed[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
static const int edges[] = {2, 1, 3, 5, 7, 4};
|
||||
static const int odds[] = {1, 3, 5, 7, 9, 11};
|
||||
static const int evens[] = {2, 4, 6, 8, 10, 12};
|
||||
static const int* const sets[] = {mixed, edges, odds, evens};
|
||||
static const std::size_t set_len[] = {10u, 6u, 6u, 6u};
|
||||
static const std::size_t sizes[] = {0u, 1u, 2u, 3u, 5u, 6u, 10u};
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sets)/sizeof(sets[0]); ++s) {
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
||||
const std::size_t n = sizes[i];
|
||||
if(n > set_len[s])
|
||||
continue;
|
||||
test_detail::for_each_shape_all<int>
|
||||
(sets[s], n, shape_filler, remove_if_count_check<is_even>());
|
||||
test_detail::for_each_shape_all<int>
|
||||
(sets[s], n, shape_filler, remove_if_count_check<is_odd>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_remove_if_segmented();
|
||||
@@ -428,5 +477,6 @@ int main()
|
||||
test_remove_if_movable_seg();
|
||||
test_remove_if_movable_seg2();
|
||||
test_remove_if_shape_matrix();
|
||||
test_remove_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -386,6 +386,52 @@ void test_remove_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.remove] mandates "Exactly last - first applications of the
|
||||
// corresponding predicate or comparison", so an element compared twice at a
|
||||
// segment boundary is a conformance failure. segmented_remove is built from
|
||||
// segmented_find plus segmented_remove_copy, and the count is what catches
|
||||
// the join between them re-testing the element find stopped on.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct remove_comparison_check
|
||||
{
|
||||
template<class C>
|
||||
void operator()(C& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_remove(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counted_int(shape_removed));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST_EQ(applied, n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_remove_comparison_count()
|
||||
{
|
||||
static const int mixed[] = {1, 2, 3, 2, 4, 2, 5, 6, 2, 7};
|
||||
static const int edges[] = {2, 1, 3, 4, 5, 2};
|
||||
static const int none[] = {1, 3, 5, 7, 9, 11};
|
||||
static const int all_rm[] = {2, 2, 2, 2, 2, 2};
|
||||
static const int* const sets[] = {mixed, edges, none, all_rm};
|
||||
static const std::size_t set_len[] = {10u, 6u, 6u, 6u};
|
||||
static const std::size_t sizes[] = {0u, 1u, 2u, 3u, 5u, 6u, 10u};
|
||||
|
||||
for(std::size_t s = 0; s != sizeof(sets)/sizeof(sets[0]); ++s) {
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i) {
|
||||
const std::size_t n = sizes[i];
|
||||
if(n > set_len[s])
|
||||
continue;
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(sets[s], n, shape_filler, remove_comparison_check());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_remove_segmented();
|
||||
@@ -407,5 +453,6 @@ int main()
|
||||
test_remove_movable_seg();
|
||||
test_remove_movable_seg2();
|
||||
test_remove_shape_matrix();
|
||||
test_remove_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -210,6 +210,49 @@ void test_replace_if_single_segment_sentinel()
|
||||
BOOST_TEST_EQ(*it, expected[i]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.replace] mandates "Exactly last - first applications of the
|
||||
// corresponding predicate", so a segment walked twice or an element skipped is
|
||||
// caught here even where the rewritten values still come out right.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct replace_if_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_replace_if(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, is_negative()), 0);
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_replace_if_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (i % 2) ? (i + 1) : -(i + 1);
|
||||
test_detail::for_each_shape_all<int>(vals, n, -6, replace_if_count_check());
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
test_detail::for_each_shape_all<int>(vals, n, -6, replace_if_count_check());
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = -(i + 1);
|
||||
test_detail::for_each_shape_all<int>(vals, n, -6, replace_if_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_replace_if_shape_matrix();
|
||||
@@ -220,5 +263,6 @@ int main()
|
||||
test_replace_if_sentinel_non_segmented();
|
||||
test_replace_if_seg2();
|
||||
test_replace_if_single_segment_sentinel();
|
||||
test_replace_if_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -177,6 +177,51 @@ void test_replace_single_segment_sentinel()
|
||||
BOOST_TEST_EQ(*it, expected[i]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.replace] mandates "Exactly last - first applications of the
|
||||
// corresponding predicate", so an element compared twice at a segment
|
||||
// boundary is a conformance failure. There is no predicate overload, so the
|
||||
// count comes from the value type.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct replace_comparison_check
|
||||
{
|
||||
int oldv;
|
||||
|
||||
explicit replace_comparison_check(int o) : oldv(o) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_replace(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counted_int(oldv), test_detail::counted_int(999));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST_EQ(applied, n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_replace_comparison_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i/2 + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
for(std::size_t v = 0; v <= n/2u + 1u; ++v) {
|
||||
const int oldv = int(v);
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, oldv, replace_comparison_check(oldv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_replace_shape_matrix();
|
||||
@@ -187,5 +232,6 @@ int main()
|
||||
test_replace_sentinel_non_segmented();
|
||||
test_replace_seg2();
|
||||
test_replace_single_segment_sentinel();
|
||||
test_replace_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -229,6 +229,40 @@ void test_reverse_shape_matrix()
|
||||
test_detail::for_each_shape_all<int>(vals, sizes[s], -999, reverse_shape_check());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Swap count.
|
||||
//
|
||||
// [alg.reverse] mandates "Exactly (last - first)/2 swaps", so a pair swapped
|
||||
// twice where the two walking ends cross a segment boundary is a conformance
|
||||
// failure -- and would also put the pair back where it started.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct reverse_swap_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_reverse(c.begin(), test_detail::iter_at(c, n));
|
||||
const std::size_t applied = test_detail::counted_int_ops().swp;
|
||||
|
||||
BOOST_TEST_EQ(applied, n/2u);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_reverse_swap_count()
|
||||
{
|
||||
int vals[16];
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 3u, 4u, 5u, 8u, 11u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s)
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, sizes[s], -999, reverse_swap_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_reverse_shape_matrix();
|
||||
@@ -240,5 +274,6 @@ int main()
|
||||
test_special_segment_conditions();
|
||||
test_reverse_movable_seg();
|
||||
test_reverse_movable_seg2();
|
||||
test_reverse_swap_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -399,6 +399,80 @@ void test_search_n_forward_run_before_last_segment()
|
||||
BOOST_TEST(segmented_search_n(sv.begin(), last, 4, 2) == last);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.search] mandates "At most last - first comparisons" for search_n --
|
||||
// LWG 714 lowered the original N * count bound, so the run scanner is not
|
||||
// allowed to re-compare the elements of an abandoned partial run. There is
|
||||
// no predicate overload, so the count comes from the value type.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct search_n_comparison_check
|
||||
{
|
||||
int count;
|
||||
int value;
|
||||
|
||||
search_n_comparison_check(int cnt, int v) : count(cnt), value(v) {}
|
||||
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_search_n(c.begin(), test_detail::iter_at(c, n), count,
|
||||
test_detail::counted_int(value));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST(applied <= n);
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void run_search_n_count_shapes(const int* vals, std::size_t n, int count, int value)
|
||||
{
|
||||
test_detail::for_each_shape_all<test_detail::counted_int>
|
||||
(vals, n, -999, search_n_comparison_check(count, value));
|
||||
test_detail::for_each_shape_all_fwd<test_detail::counted_int>
|
||||
(vals, n, -999, search_n_comparison_check(count, value));
|
||||
}
|
||||
|
||||
void test_search_n_comparison_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 3u, 5u, 8u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
std::size_t i = 0;
|
||||
int vals[16] = { 0 };
|
||||
|
||||
for(int count = 0; count <= int(n) + 1; ++count) {
|
||||
// A single run of every length at every start position.
|
||||
for(std::size_t p = 0; p != n; ++p) {
|
||||
for(std::size_t len = 1u; p + len <= n; ++len) {
|
||||
for(i = 0; i != n; ++i)
|
||||
vals[i] = (i >= p && i < p + len) ? 7 : 100 + int(i);
|
||||
run_search_n_count_shapes(vals, n, count, 7);
|
||||
}
|
||||
}
|
||||
|
||||
// Two runs separated by a single hole, so a partial run has to be
|
||||
// abandoned and restarted without looking at anything twice.
|
||||
for(std::size_t g = 0; g != n; ++g) {
|
||||
for(i = 0; i != n; ++i)
|
||||
vals[i] = (i == g) ? 100 : 7;
|
||||
run_search_n_count_shapes(vals, n, count, 7);
|
||||
}
|
||||
|
||||
for(i = 0; i != n; ++i)
|
||||
vals[i] = (i % 2u == 0u) ? 7 : 100;
|
||||
run_search_n_count_shapes(vals, n, count, 7);
|
||||
|
||||
for(i = 0; i != n; ++i)
|
||||
vals[i] = 100 + int(i);
|
||||
run_search_n_count_shapes(vals, n, count, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_search_n_shape_matrix();
|
||||
@@ -417,5 +491,6 @@ int main()
|
||||
test_search_n_single_segment_seg2_single_inner();
|
||||
test_search_n_single_segment_forward();
|
||||
test_search_n_forward_run_before_last_segment();
|
||||
test_search_n_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -557,6 +557,69 @@ void test_search_forward_match_before_last_segment()
|
||||
== test_detail::iter_at(sv, 7));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [alg.search] mandates "At most (last1 - first1) * (last2 - first2)
|
||||
// applications of the corresponding predicate". There is no predicate
|
||||
// overload, so the count comes from the value type.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct search_comparison_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& hay, std::size_t n1, const char* s1,
|
||||
C2& ndl, std::size_t n2, const char* s2) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_search(hay.begin(), test_detail::iter_at(hay, n1),
|
||||
ndl.begin(), test_detail::iter_at(ndl, n2));
|
||||
const std::size_t applied = test_detail::counted_int_ops().cmp;
|
||||
|
||||
BOOST_TEST(applied <= n1 * n2);
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void run_search_count_shapes(const int* hay, std::size_t n1, const int* ndl, std::size_t n2)
|
||||
{
|
||||
test_detail::for_each_shape2_all<test_detail::counted_int, test_detail::counted_int>
|
||||
(hay, n1, ndl, n2, -999, search_comparison_check());
|
||||
}
|
||||
|
||||
void test_search_comparison_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 8u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n1 = sizes[s];
|
||||
int hay[16] = { 0 };
|
||||
int ndl[16] = { 0 };
|
||||
std::size_t i = 0, j = 0;
|
||||
|
||||
// Periodic haystack: many candidate starts per needle, so every false
|
||||
// start has to be rejected and the re-comparisons pile up fastest.
|
||||
for(i = 0; i != n1; ++i)
|
||||
hay[i] = int(i % 3u) + 1;
|
||||
|
||||
const std::size_t maxlen = (n1 + 1u < 4u) ? n1 + 1u : 4u;
|
||||
run_search_count_shapes(hay, n1, ndl, 0u);
|
||||
|
||||
for(std::size_t len = 1u; len <= maxlen; ++len) {
|
||||
for(std::size_t off = 0; off + len <= n1; ++off) {
|
||||
for(j = 0; j != len; ++j)
|
||||
ndl[j] = hay[off + j];
|
||||
run_search_count_shapes(hay, n1, ndl, len);
|
||||
|
||||
ndl[len - 1u] = 500;
|
||||
run_search_count_shapes(hay, n1, ndl, len);
|
||||
}
|
||||
for(j = 0; j != len; ++j)
|
||||
ndl[j] = 500 + int(j);
|
||||
run_search_count_shapes(hay, n1, ndl, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_search_shape_matrix();
|
||||
@@ -580,5 +643,6 @@ int main()
|
||||
test_search_single_segment_haystack_and_needle();
|
||||
test_search_single_segment_forward();
|
||||
test_search_forward_match_before_last_segment();
|
||||
test_search_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -651,6 +651,70 @@ void test_set_difference_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [set.difference] mandates "At most 2 * ((last1 - first1) + (last2 - first2))
|
||||
// - 1 comparisons". The destination is laid out in segments of one, two and
|
||||
// three elements as well as flat, since it is the destination boundaries that
|
||||
// make the leaf return mid-merge and the walker call it again.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct less_int
|
||||
{
|
||||
bool operator()(int a, int b) const { return a < b; }
|
||||
};
|
||||
|
||||
inline std::size_t set_difference_comparison_bound(std::size_t n1, std::size_t n2)
|
||||
{
|
||||
const std::size_t total = n1 + n2;
|
||||
return total ? 2u*total - 1u : 0u;
|
||||
}
|
||||
|
||||
struct set_difference_count_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
boost::container::vector<int> flat(n1 + n2 + 1u, -1);
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_difference(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
flat.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_difference_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
for(std::size_t block = 1u; block <= 3u; ++block) {
|
||||
test_detail::seg_vector<int> out;
|
||||
for(std::size_t room = 0; room <= n1 + n2; room += block)
|
||||
out.add_segment(block, -1);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_difference(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
out.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_difference_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_set_difference_comparison_count()
|
||||
{
|
||||
const int v1[] = {1, 2, 2, 3, 5, 8};
|
||||
const int v2[] = {2, 3, 3, 4, 8, 9};
|
||||
|
||||
static const std::size_t pairs[][2] =
|
||||
{ {0u, 0u}, {0u, 3u}, {3u, 0u}, {1u, 1u}, {2u, 4u}, {4u, 2u}, {5u, 6u} };
|
||||
|
||||
for(std::size_t p = 0; p != sizeof(pairs)/sizeof(pairs[0]); ++p)
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, pairs[p][0], v2, pairs[p][1], -999, set_difference_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_set_difference_shape_matrix();
|
||||
@@ -678,5 +742,6 @@ int main()
|
||||
test_set_difference_single_segment_sentinel();
|
||||
test_set_difference_single_segment_with_comp();
|
||||
|
||||
test_set_difference_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -644,6 +644,70 @@ void test_set_intersection_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [set.intersection] mandates "At most 2 * ((last1 - first1) + (last2 -
|
||||
// first2)) - 1 comparisons". The destination is laid out in segments of one,
|
||||
// two and three elements as well as flat, since it is the destination
|
||||
// boundaries that make the leaf return mid-merge and the walker call it again.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct less_int
|
||||
{
|
||||
bool operator()(int a, int b) const { return a < b; }
|
||||
};
|
||||
|
||||
inline std::size_t set_intersection_comparison_bound(std::size_t n1, std::size_t n2)
|
||||
{
|
||||
const std::size_t total = n1 + n2;
|
||||
return total ? 2u*total - 1u : 0u;
|
||||
}
|
||||
|
||||
struct set_intersection_count_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
boost::container::vector<int> flat(n1 + n2 + 1u, -1);
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_intersection(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
flat.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_intersection_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
for(std::size_t block = 1u; block <= 3u; ++block) {
|
||||
test_detail::seg_vector<int> out;
|
||||
for(std::size_t room = 0; room <= n1 + n2; room += block)
|
||||
out.add_segment(block, -1);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_intersection(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
out.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_intersection_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_set_intersection_comparison_count()
|
||||
{
|
||||
const int v1[] = {1, 2, 2, 3, 5, 8};
|
||||
const int v2[] = {2, 3, 3, 4, 8, 9};
|
||||
|
||||
static const std::size_t pairs[][2] =
|
||||
{ {0u, 0u}, {0u, 3u}, {3u, 0u}, {1u, 1u}, {2u, 4u}, {4u, 2u}, {5u, 6u} };
|
||||
|
||||
for(std::size_t p = 0; p != sizeof(pairs)/sizeof(pairs[0]); ++p)
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, pairs[p][0], v2, pairs[p][1], -999, set_intersection_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_set_intersection_shape_matrix();
|
||||
@@ -671,5 +735,6 @@ int main()
|
||||
test_set_intersection_single_segment_sentinel();
|
||||
test_set_intersection_single_segment_with_comp();
|
||||
|
||||
test_set_intersection_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -670,6 +670,72 @@ void test_set_symmetric_difference_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [set.symmetric.difference] mandates "At most 2 * ((last1 - first1) + (last2
|
||||
// - first2)) - 1 comparisons". The destination is laid out in segments of
|
||||
// one, two and three elements as well as flat, since it is the destination
|
||||
// boundaries that make the leaf return mid-merge and the walker call it again.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct less_int
|
||||
{
|
||||
bool operator()(int a, int b) const { return a < b; }
|
||||
};
|
||||
|
||||
inline std::size_t set_symmetric_difference_comparison_bound(std::size_t n1, std::size_t n2)
|
||||
{
|
||||
const std::size_t total = n1 + n2;
|
||||
return total ? 2u*total - 1u : 0u;
|
||||
}
|
||||
|
||||
struct set_symmetric_difference_count_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
boost::container::vector<int> flat(n1 + n2 + 1u, -1);
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_symmetric_difference
|
||||
(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
flat.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_symmetric_difference_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
for(std::size_t block = 1u; block <= 3u; ++block) {
|
||||
test_detail::seg_vector<int> out;
|
||||
for(std::size_t room = 0; room <= n1 + n2; room += block)
|
||||
out.add_segment(block, -1);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_symmetric_difference
|
||||
(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
out.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_symmetric_difference_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_set_symmetric_difference_comparison_count()
|
||||
{
|
||||
const int v1[] = {1, 2, 2, 3, 5, 8};
|
||||
const int v2[] = {2, 3, 3, 4, 8, 9};
|
||||
|
||||
static const std::size_t pairs[][2] =
|
||||
{ {0u, 0u}, {0u, 3u}, {3u, 0u}, {1u, 1u}, {2u, 4u}, {4u, 2u}, {5u, 6u} };
|
||||
|
||||
for(std::size_t p = 0; p != sizeof(pairs)/sizeof(pairs[0]); ++p)
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, pairs[p][0], v2, pairs[p][1], -999, set_symmetric_difference_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_set_symmetric_difference_shape_matrix();
|
||||
@@ -697,5 +763,6 @@ int main()
|
||||
test_set_symmetric_difference_single_segment_sentinel();
|
||||
test_set_symmetric_difference_single_segment_with_comp();
|
||||
|
||||
test_set_symmetric_difference_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -679,6 +679,70 @@ void test_set_union_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Comparison count.
|
||||
//
|
||||
// [set.union] mandates "At most 2 * ((last1 - first1) + (last2 - first2)) - 1
|
||||
// comparisons". The destination is laid out in segments of one, two and three
|
||||
// elements as well as flat, since it is the destination boundaries that make
|
||||
// the leaf return mid-merge and the walker call it again.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct less_int
|
||||
{
|
||||
bool operator()(int a, int b) const { return a < b; }
|
||||
};
|
||||
|
||||
inline std::size_t set_union_comparison_bound(std::size_t n1, std::size_t n2)
|
||||
{
|
||||
const std::size_t total = n1 + n2;
|
||||
return total ? 2u*total - 1u : 0u;
|
||||
}
|
||||
|
||||
struct set_union_count_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
boost::container::vector<int> flat(n1 + n2 + 1u, -1);
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_union(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
flat.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_union_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
for(std::size_t block = 1u; block <= 3u; ++block) {
|
||||
test_detail::seg_vector<int> out;
|
||||
for(std::size_t room = 0; room <= n1 + n2; room += block)
|
||||
out.add_segment(block, -1);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_set_union(c1.begin(), test_detail::iter_at(c1, n1),
|
||||
c2.begin(), test_detail::iter_at(c2, n2),
|
||||
out.begin(), test_detail::counting_pred(calls, less_int()));
|
||||
BOOST_TEST(calls.n <= set_union_comparison_bound(n1, n2));
|
||||
}
|
||||
|
||||
BOOST_TEST(s1 != 0 && s2 != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_set_union_comparison_count()
|
||||
{
|
||||
const int v1[] = {1, 2, 2, 3, 5, 8};
|
||||
const int v2[] = {2, 3, 3, 4, 8, 9};
|
||||
|
||||
static const std::size_t pairs[][2] =
|
||||
{ {0u, 0u}, {0u, 3u}, {3u, 0u}, {1u, 1u}, {2u, 4u}, {4u, 2u}, {5u, 6u} };
|
||||
|
||||
for(std::size_t p = 0; p != sizeof(pairs)/sizeof(pairs[0]); ++p)
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(v1, pairs[p][0], v2, pairs[p][1], -999, set_union_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_set_union_shape_matrix();
|
||||
@@ -706,5 +770,6 @@ int main()
|
||||
test_set_union_single_segment_sentinel();
|
||||
test_set_union_single_segment_with_comp();
|
||||
|
||||
test_set_union_comparison_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -215,6 +215,53 @@ void test_stable_partition_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Predicate application count.
|
||||
//
|
||||
// [alg.partitions] mandates that stable_partition performs "Exactly N
|
||||
// applications of the predicate", N = last - first, however much extra memory
|
||||
// it uses and however the range is segmented.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct stable_partition_count_check
|
||||
{
|
||||
template<class Cont>
|
||||
void operator()(Cont& c, std::size_t n, const char* spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_stable_partition(c.begin(), test_detail::iter_at(c, n),
|
||||
test_detail::counting_pred(calls, is_even()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
BOOST_TEST(test_detail::filler_intact(c, n, -999));
|
||||
BOOST_TEST(spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_stable_partition_predicate_count()
|
||||
{
|
||||
const std::size_t sizes[] = { 0u, 1u, 2u, 5u, 12u };
|
||||
for(std::size_t s = 0; s != sizeof(sizes)/sizeof(sizes[0]); ++s) {
|
||||
const std::size_t n = sizes[s];
|
||||
int vals[16];
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = i + 1;
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, stable_partition_count_check());
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = 2*i + 2;
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, stable_partition_count_check());
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = 2*i + 1;
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, stable_partition_count_check());
|
||||
|
||||
for(int i = 0; i != 16; ++i)
|
||||
vals[i] = (i < 8) ? 2*i + 1 : 2*i + 2;
|
||||
test_detail::for_each_shape_all<int>(vals, n, -999, stable_partition_count_check());
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_stable_partition_shape_matrix();
|
||||
@@ -225,5 +272,6 @@ int main()
|
||||
test_stable_partition_non_segmented();
|
||||
test_stable_partition_movable_seg();
|
||||
test_stable_partition_movable_seg2();
|
||||
test_stable_partition_predicate_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -552,6 +552,40 @@ void test_swap_ranges_shape_matrix()
|
||||
shape_filler, swap_ranges_shape_check());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Swap count.
|
||||
//
|
||||
// [alg.swap] mandates "Exactly last1 - first1 swaps", so a pair swapped twice
|
||||
// at a segment boundary is a conformance failure -- and, unlike a re-read, it
|
||||
// would also put the values back where they started.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct swap_ranges_count_check
|
||||
{
|
||||
template<class C1, class C2>
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
test_detail::counted_int_ops().reset();
|
||||
segmented_swap_ranges(c1.begin(), test_detail::iter_at(c1, n1), c2.begin());
|
||||
const std::size_t applied = test_detail::counted_int_ops().swp;
|
||||
|
||||
BOOST_TEST_EQ(applied, n1);
|
||||
BOOST_TEST(s1 != 0 && s2 != 0 && n2 >= n1);
|
||||
}
|
||||
};
|
||||
|
||||
void test_swap_ranges_swap_count()
|
||||
{
|
||||
static const std::size_t sizes[][2] =
|
||||
{ {0, 0}, {1, 1}, {1, 3}, {2, 2}, {3, 3}, {3, 6}, {5, 5}, {6, 10} };
|
||||
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i)
|
||||
test_detail::for_each_shape2_all<test_detail::counted_int, test_detail::counted_int>
|
||||
(shape_first_vals, sizes[i][0], shape_second_vals, sizes[i][1],
|
||||
shape_filler, swap_ranges_count_check());
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_swap_ranges_full();
|
||||
@@ -575,5 +609,6 @@ int main()
|
||||
test_swap_ranges_single_segment_second_from_flat();
|
||||
|
||||
test_swap_ranges_shape_matrix();
|
||||
test_swap_ranges_swap_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,98 @@ public:
|
||||
friend std::ostream& operator<<(std::ostream& os, const movable_int& m) { return os << m.val_; }
|
||||
};
|
||||
|
||||
//! Applications of a function object, counted outside it. The algorithms take
|
||||
//! their function objects by value and copy them through several dispatch
|
||||
//! layers, so a counter kept as a member of the functor would be incremented in
|
||||
//! a copy the test never sees again.
|
||||
struct op_counter
|
||||
{
|
||||
std::size_t n;
|
||||
|
||||
op_counter() : n(0) {}
|
||||
void reset() { n = 0; }
|
||||
};
|
||||
|
||||
//! Wraps a callable so that every application bumps an op_counter owned by the
|
||||
//! caller. Result is the wrapped callable's return type, which C++03 cannot
|
||||
//! deduce; counting_pred() supplies it for the bool-returning common case.
|
||||
template<class F, class Result>
|
||||
class counting_fun
|
||||
{
|
||||
op_counter* c_;
|
||||
F f_;
|
||||
|
||||
public:
|
||||
counting_fun(op_counter& c, F f) : c_(&c), f_(f) {}
|
||||
|
||||
Result operator()() const
|
||||
{ ++c_->n; return f_(); }
|
||||
|
||||
template<class T>
|
||||
Result operator()(T& x) const
|
||||
{ ++c_->n; return f_(x); }
|
||||
|
||||
template<class T, class U>
|
||||
Result operator()(T& x, U& y) const
|
||||
{ ++c_->n; return f_(x, y); }
|
||||
};
|
||||
|
||||
//! Predicates and comparisons all return bool, so their result type does not
|
||||
//! have to be spelled out at every call site.
|
||||
template<class F>
|
||||
counting_fun<F, bool> counting_pred(op_counter& c, F f)
|
||||
{ return counting_fun<F, bool>(c, f); }
|
||||
|
||||
//! Operations counted_int performs. The algorithms specified in terms of
|
||||
//! comparisons, assignments or swaps of the element type take a value rather
|
||||
//! than a function object, so there is nothing for the test to wrap and the
|
||||
//! counters have to be reachable from the type's own operators.
|
||||
struct value_op_counter
|
||||
{
|
||||
std::size_t cmp;
|
||||
std::size_t assign;
|
||||
std::size_t swp;
|
||||
|
||||
value_op_counter() : cmp(0), assign(0), swp(0) {}
|
||||
void reset() { cmp = 0; assign = 0; swp = 0; }
|
||||
};
|
||||
|
||||
inline value_op_counter& counted_int_ops()
|
||||
{
|
||||
static value_op_counter c;
|
||||
return c;
|
||||
}
|
||||
|
||||
//! int whose comparisons, assignments and swaps bump counted_int_ops().
|
||||
//! Construction deliberately does not, so that building a range leaves the
|
||||
//! counters alone and only the algorithm under test has to be bracketed by a
|
||||
//! reset.
|
||||
class counted_int
|
||||
{
|
||||
int v_;
|
||||
|
||||
public:
|
||||
counted_int(int v = 0) : v_(v) {}
|
||||
counted_int(const counted_int& o) : v_(o.v_) {}
|
||||
|
||||
int value() const { return v_; }
|
||||
|
||||
counted_int& operator=(const counted_int& o)
|
||||
{ ++counted_int_ops().assign; v_ = o.v_; return *this; }
|
||||
|
||||
friend bool operator==(const counted_int& a, const counted_int& b)
|
||||
{ ++counted_int_ops().cmp; return a.v_ == b.v_; }
|
||||
|
||||
friend bool operator!=(const counted_int& a, const counted_int& b)
|
||||
{ ++counted_int_ops().cmp; return a.v_ != b.v_; }
|
||||
|
||||
friend void swap(counted_int& a, counted_int& b)
|
||||
{ ++counted_int_ops().swp; const int t = a.v_; a.v_ = b.v_; b.v_ = t; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const counted_int& c)
|
||||
{ return os << c.v_; }
|
||||
};
|
||||
|
||||
// Cat selects the advertised iterator category. Algorithms such as
|
||||
// segmented_find_last have distinct segmented implementations for forward and
|
||||
// for bidirectional iterators, so both have to be instantiable from a test.
|
||||
@@ -436,6 +528,20 @@ public:
|
||||
// true while the guard still holds filler, i.e. the
|
||||
// algorithm has not written past the end.
|
||||
//
|
||||
// Operation counting, for the complexity requirements stated as a number of
|
||||
// applications of a predicate, comparison or operation.
|
||||
// op_counter a counter living outside the function object.
|
||||
// counting_fun<F, R>(c, f)
|
||||
// f, with every application counted in c. R is f's
|
||||
// return type.
|
||||
// counting_pred(c, f) the same for a bool-returning f, whose result type
|
||||
// therefore needs no spelling out.
|
||||
// counted_int element type whose comparisons, assignments and
|
||||
// swaps are counted, for the algorithms that take a
|
||||
// value instead of a function object.
|
||||
// counted_int_ops() the cmp/assign/swp counters counted_int bumps;
|
||||
// reset() them immediately before the call.
|
||||
//
|
||||
// Combinators. Each one walks the core m/s specs first, in their original
|
||||
// order, and then the empty-segment ones. Each builds every range afresh for
|
||||
// every combination, so a callable is free to mutate the ranges it is handed.
|
||||
@@ -587,6 +693,7 @@ typename Cont::iterator iter_at(Cont& c, std::size_t n)
|
||||
|
||||
inline int seg_value_of(int v) { return v; }
|
||||
inline int seg_value_of(const movable_int& m) { return m.value(); }
|
||||
inline int seg_value_of(const counted_int& c) { return c.value(); }
|
||||
|
||||
template<class It>
|
||||
boost::container::vector<int> flatten_ints(It first, It last)
|
||||
|
||||
@@ -446,6 +446,61 @@ void test_transform_shape_matrix()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Operation application count.
|
||||
//
|
||||
// [alg.transform] mandates "Exactly last1 - first1 applications of op",
|
||||
// whatever the segmentation of either range, so an element handed to op again
|
||||
// after the destination walker steps to the next segment is caught here.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct transform_count_check
|
||||
{
|
||||
template<class CSrc, class CDst>
|
||||
void operator()(CSrc& src, std::size_t n_src, const char* src_spec,
|
||||
CDst& dst, std::size_t n_dst, const char* dst_spec) const
|
||||
{
|
||||
test_detail::op_counter calls;
|
||||
segmented_transform(src.begin(), test_detail::iter_at(src, n_src), dst.begin(),
|
||||
test_detail::counting_fun<times_two, int>(calls, times_two()));
|
||||
|
||||
BOOST_TEST_EQ(calls.n, n_src);
|
||||
BOOST_TEST(test_detail::filler_intact(dst, n_dst, shape_filler));
|
||||
BOOST_TEST(src_spec != 0 && dst_spec != 0);
|
||||
}
|
||||
};
|
||||
|
||||
void test_transform_operation_count()
|
||||
{
|
||||
static const std::size_t sizes[][2] =
|
||||
{ {0, 0}, {1, 1}, {1, 3}, {2, 2}, {3, 3}, {3, 6}, {5, 5}, {6, 10} };
|
||||
|
||||
for(std::size_t i = 0; i != sizeof(sizes)/sizeof(sizes[0]); ++i)
|
||||
test_detail::for_each_shape2_all<int, int>
|
||||
(shape_src_vals, sizes[i][0], shape_dst_vals, sizes[i][1],
|
||||
shape_filler, transform_count_check());
|
||||
|
||||
// Destination segments of a handful of elements each, so that boundary
|
||||
// crossings dominate rather than being a single mid-range event.
|
||||
static const std::size_t blocks[] = {1u, 2u, 3u, 8u, 16u};
|
||||
const std::size_t n = 64u;
|
||||
boost::container::vector<int> in;
|
||||
in.reserve(n);
|
||||
for(std::size_t i = 0; i != n; ++i)
|
||||
in.push_back(static_cast<int>(i) + 1);
|
||||
|
||||
for(std::size_t b = 0; b != sizeof(blocks)/sizeof(blocks[0]); ++b) {
|
||||
test_detail::seg_vector<int> out;
|
||||
for(std::size_t room = 0; room < n; room += blocks[b])
|
||||
out.add_segment(blocks[b], 0);
|
||||
|
||||
test_detail::op_counter calls;
|
||||
segmented_transform(in.begin(), in.end(), out.begin(),
|
||||
test_detail::counting_fun<times_two, int>(calls, times_two()));
|
||||
BOOST_TEST_EQ(calls.n, n);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_transform_full_range();
|
||||
@@ -468,5 +523,6 @@ int main()
|
||||
test_transform_single_segment_dst_from_flat();
|
||||
|
||||
test_transform_shape_matrix();
|
||||
test_transform_operation_count();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user