diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /vespalib/src/tests/left_right_heap/left_right_heap_test.cpp |
Publish
Diffstat (limited to 'vespalib/src/tests/left_right_heap/left_right_heap_test.cpp')
-rw-r--r-- | vespalib/src/tests/left_right_heap/left_right_heap_test.cpp | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/vespalib/src/tests/left_right_heap/left_right_heap_test.cpp b/vespalib/src/tests/left_right_heap/left_right_heap_test.cpp new file mode 100644 index 00000000000..f06d0534ef6 --- /dev/null +++ b/vespalib/src/tests/left_right_heap/left_right_heap_test.cpp @@ -0,0 +1,271 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/util/left_right_heap.h> +#include <vespa/vespalib/util/stringfmt.h> +#include <stdlib.h> +#include <algorithm> +#include <vector> + +using namespace vespalib; + +//----------------------------------------------------------------------------- + +typedef std::unique_ptr<int> int_up; + +template <typename T> T wrap(int value); +template <> int wrap<int>(int value) { return value; } +template <> int_up wrap<int_up>(int value) { return int_up(new int(value)); } + +int unwrap(const int &value) { return value; } +int unwrap(const int_up &value) { return *value; } + +// verbose types needed to avoid warning + +struct CmpInt { + bool operator()(const int &a, const int &b) const { + return (a < b); + } +}; + +struct CmpIntUp { + bool operator()(const int_up &a, const int_up &b) const { + return (*a < *b); + } +}; + +//----------------------------------------------------------------------------- + +template <typename Heap> struct IsRight { enum { VALUE = 0 }; }; +template <> struct IsRight<RightHeap> { enum { VALUE = 1 }; }; +template <> struct IsRight<RightArrayHeap> { enum { VALUE = 1 }; }; + +bool operator==(const std::vector<int_up> &a, + const std::vector<int> &b) +{ + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (*a[i] != b[i]) { + return false; + } + } + return true; +} + +size_t _G_InputSize = 1000; + +struct Input { + size_t n; + std::vector<int> data; + Input() : n(_G_InputSize), data() { + srandom(42); + for (size_t i = 0; i < n; ++i) { + data.push_back(random()); + } + ASSERT_EQUAL(n, data.size()); + } +}; + + +template <typename Heap, typename Value = int, typename Cmp = CmpInt> +struct Setup { + typedef Setup<Heap, int_up, CmpIntUp> IUP; + Input &input; + std::vector<Value> data; + Cmp cmp; + size_t limit; + Setup(Input &i) : input(i), data(), cmp(), limit(0) {} + + static void dumpData(Value *begin, Value *end) { + int n = 10; + while ((end - begin) > n) { + for (int i = 0; i < n; ++i) { + fprintf(stderr, "%d, ", unwrap(*begin++)); + } + fprintf(stderr, "\n"); + } + while ((end - begin) > 0) { + fprintf(stderr, "%d, ", unwrap(*begin++)); + } + fprintf(stderr, "\n"); + } + + static int peek_at(Value *begin, Value *end, size_t idx) { + if (&Heap::template front(begin, end) == begin) { + return unwrap(*(begin + idx)); // normal order + } else { + return unwrap(*(end - 1 - idx)); // inverted order + } + } + + static void checkHeap(Value *begin, Value *end) { + size_t len = (end - begin); + for (size_t i = 0; i < len; ++i) { + size_t child1 = (2 * i) + 1; + size_t child2 = (2 * i) + 2; + if (child1 < len) { + if(!EXPECT_LESS_EQUAL(peek_at(begin, end, i), + peek_at(begin, end, child1))) + { + dumpData(begin, end); + TEST_FATAL("forced unwind (see previous failure)"); + } + } + if (child2 < len) { + if (!EXPECT_LESS_EQUAL(peek_at(begin, end, i), + peek_at(begin, end, child2))) + { + dumpData(begin, end); + TEST_FATAL("forced unwind (see previous failure)"); + } + } + } + } + + void push() { + if (IsRight<Heap>::VALUE) { + ASSERT_GREATER(limit, 0u); + Heap::push(&data[--limit], &data[data.size()], cmp); + } else { + ASSERT_LESS(limit, data.size()); + Heap::push(&data[0], &data[++limit], cmp); + } + } + void push(int value) { + if (IsRight<Heap>::VALUE) { + data[limit - 1] = wrap<Value>(value); + } else { + data[limit] = wrap<Value>(value); + } + push(); + } + Value &front() { + if (IsRight<Heap>::VALUE) { + return Heap::front(&data[limit], &data[data.size()]); + } else { + return Heap::front(&data[0], &data[limit]); + } + } + void adjust() { + if (IsRight<Heap>::VALUE) { + Heap::adjust(&data[limit], &data[data.size()], cmp); + } else { + Heap::adjust(&data[0], &data[limit], cmp); + } + } + int pop() { + if (IsRight<Heap>::VALUE) { + ASSERT_LESS(limit, data.size()); + Heap::pop(&data[limit++], &data[data.size()], cmp); + return unwrap(data[limit - 1]); + } else { + ASSERT_GREATER(limit, 0u); + Heap::pop(&data[0], &data[limit--], cmp); + return unwrap(data[limit]); + } + } + void check() { + if (IsRight<Heap>::VALUE) { + checkHeap(&data[limit], &data[data.size()]); + } else { + checkHeap(&data[0], &data[limit]); + } + } + void init() { + data.clear(); + for (size_t i = 0; i < input.data.size(); ++i) { + data.push_back(wrap<Value>(input.data[i])); + } + if (IsRight<Heap>::VALUE) { + limit = data.size(); + } else { + limit = 0; + } + } + void testBasic() { + init(); + push(100); + EXPECT_EQUAL(100, unwrap(front())); + adjust(); + EXPECT_EQUAL(100, unwrap(front())); + push(50); + EXPECT_EQUAL(50, unwrap(front())); + adjust(); + EXPECT_EQUAL(50, unwrap(front())); + push(200); + push(175); + EXPECT_EQUAL(50, unwrap(front())); + front() = wrap<Value>(150); + adjust(); + EXPECT_EQUAL(100, unwrap(front())); + EXPECT_EQUAL(100, pop()); + EXPECT_EQUAL(150, pop()); + EXPECT_EQUAL(175, pop()); + EXPECT_EQUAL(200, pop()); + } + void testSort() { + init(); + for (size_t i = 0; i < input.n; ++i) { + push(); + adjust(); // has no effect here + check(); + } + for (size_t i = 0; i < input.n; ++i) { + adjust(); // has no effect here + pop(); + check(); + } + std::vector<int> ref = input.data; + EXPECT_FALSE(data == ref); + if (IsRight<Heap>::VALUE) { + std::sort(ref.begin(), ref.end(), std::less<int>()); + } else { + std::sort(ref.begin(), ref.end(), std::greater<int>()); + } + if (!EXPECT_TRUE(data == ref)) { + if (data.size() == ref.size()) { + for (size_t i = 0; i < ref.size(); ++i) { + if (unwrap(data[i]) != ref[i]) { + fprintf(stderr, "data[%zu] != %d, ref[%zu] = %d\n", + i, unwrap(data[i]), i, ref[i]); + } + } + } else { + fprintf(stderr, "sizes differ: %zu, %zu\n", data.size(), ref.size()); + } + TEST_FATAL("forced unwind (see previous failure)"); + } + } + void test() { + testBasic(); + testSort(); + } +}; + +TEST("require correct heap tags") { + LeftHeap::require_left_heap(); + RightHeap::require_right_heap(); + LeftArrayHeap::require_left_heap(); + RightArrayHeap::require_right_heap(); + LeftStdHeap::require_left_heap(); +} + +TEST_FF("verify left heap invariants and sorting", Input, Setup<LeftHeap>(f1)) { f2.test(); } +TEST_FF("verify right heap invariants and sorting", Input, Setup<RightHeap>(f1)) { f2.test(); } +TEST_FF("verify left array heap invariants and sorting", Input, Setup<LeftArrayHeap>(f1)) { f2.test(); } +TEST_FF("verify right array heap invariants and sorting", Input, Setup<RightArrayHeap>(f1)) { f2.test(); } +TEST_FF("verify left std heap invariants and sorting", Input, Setup<LeftStdHeap>(f1)) { f2.test(); } + +TEST_FF("verify [move only] left heap invariants and sorting", Input, Setup<LeftHeap>::IUP(f1)) { f2.test(); } +TEST_FF("verify [move only] right heap invariants and sorting", Input, Setup<RightHeap>::IUP(f1)) { f2.test(); } +TEST_FF("verify [move only] left array heap invariants and sorting", Input, Setup<LeftArrayHeap>::IUP(f1)) { f2.test(); } +TEST_FF("verify [move only] right array heap invariants and sorting", Input, Setup<RightArrayHeap>::IUP(f1)) { f2.test(); } +TEST_FF("verify [move only] left std heap invariants and sorting", Input, Setup<LeftStdHeap>::IUP(f1)) { f2.test(); } + +TEST_MAIN() { + // Would be nice to have access to arguments..... + _G_InputSize = 1000; // strtoul(_argv[1], NULL, 0); + TEST_RUN_ALL(); +} |