diff options
author | Scott Blomquist <sblom@microsoft.com> | 2013-01-17 17:48:09 -0800 |
---|---|---|
committer | Scott Blomquist <sblom@microsoft.com> | 2013-01-17 17:48:09 -0800 |
commit | a0f2bbe9acce9483163de730341cf89ec0331d2b (patch) | |
tree | ad38c78a7f0e916051edce90374586961dcdfee4 /Ix/CPP/unittest/testbench.cpp | |
parent | 6aa4590ea5952d3ba5e1f99b6ede76d856f88617 (diff) | |
download | RxCpp-a0f2bbe9acce9483163de730341cf89ec0331d2b.tar.gz |
New directory structure.
Diffstat (limited to 'Ix/CPP/unittest/testbench.cpp')
-rw-r--r-- | Ix/CPP/unittest/testbench.cpp | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/Ix/CPP/unittest/testbench.cpp b/Ix/CPP/unittest/testbench.cpp new file mode 100644 index 0000000..7788e5f --- /dev/null +++ b/Ix/CPP/unittest/testbench.cpp @@ -0,0 +1,527 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#include <iostream> +#include <iomanip> +#include <vector> +#include <functional> +#include <algorithm> +#include <numeric> +#include <iterator> +#include <string> + +#include <ctime> + +#include <boost/lambda/core.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/iterator.hpp> + +#include "cpplinq/linq.hpp" + +#include "testbench.hpp" + +using namespace std; +using namespace cpplinq; + +struct int_iter + : std::iterator<std::random_access_iterator_tag, int, ptrdiff_t, int*, int> +{ + int_iter(value_type i = 0, value_type step = 1) : value(i), step(step) + {} + + value_type operator*() const { + return value; + } + int_iter& operator++() { + value+=step; return *this; + } + int_iter& operator--() { + value-=step; return *this; + } + int_iter& operator+=(ptrdiff_t offset) { + value += step*offset; return *this; + } + int_iter& operator-=(ptrdiff_t offset) { + value -= step*offset; return *this; + } + ptrdiff_t operator-(int_iter rhs) const { + return ptrdiff_t((value - rhs.value)/step); + } + bool operator==(int_iter other) const { + return value == other.value; + } + bool operator!=(int_iter other) const { + return !(*this == other); + } + bool operator<(int_iter other) const { return (*this - other) < 0; } + bool operator>(int_iter other) const { return (*this - other) > 0; } + bool operator<=(int_iter other) const { return (*this - other) <= 0; } + bool operator>=(int_iter other) const { return (*this - other) >= 0; } + + value_type value; + value_type step; +}; +int_iter operator+(int_iter lhs, ptrdiff_t rhs) { + return lhs+=rhs; +} +int_iter operator+(ptrdiff_t lhs, int_iter rhs) { + return rhs+=lhs; +} +int_iter operator-(int_iter lhs, ptrdiff_t rhs) { + return lhs-=rhs; +} +struct int_range +{ + typedef int_iter iterator; + typedef int_iter::value_type value_type; + int_range(value_type begin, value_type end, value_type step = 1) + : b(begin, step), e(end, step) + { + if (step == 0) { + throw std::logic_error("bad step"); + } + if (abs(end - begin) % abs(step) != 0) { + end -= (end-begin)%abs(step); + } + } + int_iter begin() const { return int_iter(b);} + int_iter end() const { return int_iter(e); } + + iterator b, e; +}; + +vector<int> vector_range(int start, int end) +{ + vector<int> v; + for (int i = start; i < end; ++i) + v.push_back(i); + return v; +} + +TEST(test_selection) +{ + vector<int> v = vector_range(0, 10); + + auto v2 = from(v) + .select([](int x) { return x*2; }); + + auto result = accumulate(begin(v2), end(v2), int(0)); + + VERIFY_EQ(90, result); +} + +TEST(test_where) +{ + vector<int> v = vector_range(0, 10); + auto v2 = from(v) + .where([](int x) { return x % 2;}); + + VERIFY_EQ(1, *v2.begin()); + + auto result = accumulate(begin(v2), end(v2), int(0)); + + VERIFY_EQ(25, result); +} + + +bool is_prime(int x) { + if (x < 2) {return false;} + if (x == 2) {return true;} + for (int i = x/2; i >= 2; --i) { + if (x % i == 0) { return false;} + } + return true; +}; + +template <class It> +void display(It start, It end) +{ + int i = 0; + for_each(start, end, [&](typename iterator_traits<It>::value_type x){ + if (++i % 10 == 0) { + cout << endl; + } + cout << x << " "; + }); + cout << endl; +} + +TEST(test_whereselect) +{ + auto xs = int_range(0,100); + auto ys = from(xs) + .where(is_prime) + .select([](int x){ return x*x; }); + auto result = accumulate(begin(ys), end(ys), int(0)); + + //display(begin(ys), end(ys)); + + // primes < 100 + VERIFY_EQ(65796, result); +} +TEST(test_where_modification) +{ + vector<int> xs = vector_range(0, 100); + + auto ys = from(xs) + .where(is_prime); + std::fill(begin(ys), end(ys), int(0)); + + auto result = accumulate(begin(xs), end(xs), int(0)); + + //display(begin(ys), end(ys)); + + // non-primes < 100 + VERIFY_EQ(3890, result); +} + +TEST(test_where_any) +{ + using namespace boost::lambda; + + vector<int> xs(200); + fill(begin(xs), end(xs), int(0)); + auto it = xs.begin(); + *it = 2; + + for(;;) { + auto last = *it++; + auto primes = from(int_range(last+1, -1)) + .where([&](int x){ + return from(begin(xs), it) + //.all([&](int d){return x%d;}); + .all(x % boost::lambda::_1); + }); + *it = *primes.begin(); + if ((*it)>=100) { + break; + } + }; + xs.erase(it, xs.end()); + + auto result = accumulate(begin(xs), end(xs), int(0)); + + //display(begin(xs), end(xs)); + + // primes < 100 + VERIFY_EQ(1060, result); +} + +TEST(test_take) +{ + auto zero_one = from(int_range(0, -1)) + .take(2); + + VERIFY_EQ(0, zero_one[0]); + VERIFY_EQ(1, zero_one[1]); + + auto ten = from(int_range(0, -1)) + .skip(10); + + VERIFY_EQ(10, ten[0]); + VERIFY_EQ(11, ten[1]); +} + +vector<int> some_primes(size_t howMany) +{ + auto xs = from(int_range(0, -1)) + .where(is_prime) + .take(howMany); + auto v = vector<int>(begin(xs), end(xs)); + return v; +} + +TEST(test_groupby) +{ + auto xs = some_primes(40); + //display(begin(xs), end(xs)); + + auto grouped = + from(xs) + .groupby([](int i){return i % 10; }); + + VERIFY_EQ(6, from(grouped).count()); + for(auto group = begin(grouped); group != end(grouped); ++group) { + //cout << "key = " << group->key << endl + // << "| "; + for (auto elem = group->begin(); elem != group->end(); ++elem) { + //cout << *elem << " "; + } + //cout << endl; + + switch(group->key) { + case 2: VERIFY_EQ(1, from(*group).count()); break; + case 3: VERIFY_EQ(11, from(*group).count()); break; + case 5: VERIFY_EQ(1, from(*group).count()); break; + case 7: VERIFY_EQ(11, from(*group).count()); break; + case 1: VERIFY_EQ(8, from(*group).count()); break; + case 9: VERIFY_EQ(8, from(*group).count()); break; + } + } +} + +TEST(test_symbolname) +{ + auto complexQuery = + from(int_range(0,100000)) + .select([](int x){ return x*2;}) + .where([](int x){ return x%7; }) + .skip(20); + + //cout << " type name: " << typeid(complexQuery1).name() << endl; + + + //auto complexQuery = + // complexQuery1.groupby([](int x) { return x%5; }) + // .take(3) + // ; + + + cout << "type name: " << typeid(complexQuery).name() << endl; + cout << "type name length: " << strlen(typeid(complexQuery).name()) << endl; + + auto iter = complexQuery.begin(); + cout << "iterator name: " << typeid(iter).name() << endl; + cout << "iterator name length: " << strlen(typeid(iter).name()) << endl; +} + +TEST(test_cast) +{ + auto q = from(int_range(0,10)) + .cast<bool>(); + VERIFY_EQ(false, q[0]); + VERIFY_EQ(true, q[1]); + VERIFY_EQ(true, q[2]); + VERIFY((std::is_same<decltype(q[0]), bool>::value)); +} + +TEST(test_contains) +{ + auto q = from(int_range(0,10)) + .select([](int x){return x*2;}); + VERIFY(q.contains(4)); + VERIFY(!q.contains(5)); +} + +TEST(test_element_accessors) +{ + vector<int> v(int_iter(0), int_iter(10)); + auto q = from(v) + .where([](int x){return x%2==0;}); + + VERIFY_EQ(0, q.first()); + VERIFY_EQ(8, q.last()); + VERIFY_EQ(6, q.element_at(3)); + + bool thrown = false; + try { q.element_at(5); } catch (std::logic_error&) { thrown = true; } + VERIFY(thrown); + + q.first() = 1; + q.last() = 42; + + // note: because the vector now contains { 1, 1, 2, 3, ... 7, 8, 42 }, the first + // even number is now '2' + VERIFY_EQ(2, q.first()); + VERIFY_EQ(42, q.last()); +} + +//////////////////// New style cursors //////////////////// + +TEST(test_cursor_dynamic) +{ + dynamic_cursor<int> dc(int_iter(0), int_iter(2)); + + VERIFY(!dc.empty()); + VERIFY_EQ(0, dc.get()); + dc.inc(); + VERIFY_EQ(1, dc.get()); + dc.inc(); + VERIFY(dc.empty()); +} + +TEST(test_selectmany) +{ + int_range range1(0, 3); + auto range2 = + + from(range1) + .select_many( + [](int x) + { + return int_range(0, x+1); + }); + + auto cur = range2.get_cursor(); + + // expected: 0, 0, 1, 0, 1, 2. + VERIFY(!cur.empty()); + + VERIFY_EQ(0, cur.get()); + cur.inc(); + VERIFY(!cur.empty()); + + VERIFY_EQ(0, cur.get()); + cur.inc(); + VERIFY(!cur.empty()); + + VERIFY_EQ(1, cur.get()); + cur.inc(); + VERIFY(!cur.empty()); + + VERIFY_EQ(0, cur.get()); + cur.inc(); + VERIFY(!cur.empty()); + + VERIFY_EQ(1, cur.get()); + cur.inc(); + VERIFY(!cur.empty()); + + VERIFY_EQ(2, cur.get()); + cur.inc(); + VERIFY(cur.empty()); +} + +TEST(test_cursor_selectmany2) +{ + int_range range1(0, 3); + auto range2 = from(range1) + .select_many( + [](int x) + { + return int_range(0, x+1); + }); + + // expected: 0, 0, 1, 0, 1, 2. + int expect[] = { 0, 0, 1, 0, 1, 2 }; + + VERIFY_EQ(_countof(expect), std::distance(range2.begin(), range2.end())); + VERIFY_EQ(_countof(expect), std::distance(range2.begin(), range2.end())); + + auto result = std::mismatch(expect, expect + _countof(expect), range2.begin()); + if (result.second != range2.end()) { + cout << "mismatch: " << *result.first << " != " << *result.second << endl; + } + VERIFY( result.second == range2.end()); +} + +TEST(test_late_bind) +{ + int_range range1(0, 100); + linq_driver<dynamic_collection<int>> range2 = from(range1).late_bind(); + + VERIFY_EQ(1, range2.element_at(1)); + + auto q1 = from(range1).select([](int x){ return x*2; }).where([](int x){ return x%10!=0; }); + + cout << "typeof q1 ==> " << typeid(q1).name() << endl; + cout << "typeof q1.late_bind() ==> " << typeid(q1.late_bind()).name() << endl; +} + +struct stopwatch +{ + time_t t0, t1; + void start() { + t1 = t0 = clock(); + } + void stop() { + t1 = clock(); + } + double value() const { + return double(t1-t0)/CLOCKS_PER_SEC; + } +}; + +template <class Fn> +void test_perf(Fn fn) +{ + // warmup + fn(10); + + int n = 100; + stopwatch sw; + for(;;) + { + cout << "trying n=" << n << endl; + sw.start(); + fn(n); + sw.stop(); + if (sw.value() > 2.0) { + break; + } + n *= 2; + } + cout << "time = " << sw.value() << " s\n"; + cout << "steps = " << n << "\n"; + cout << "t/step = " << (sw.value() * 1e9 / n) << " ns\n"; + cout << "step/t = " << (n / sw.value()) << " Hz\n"; +} + +TEST(test_performance) +{ + // http://projecteuler.net/problem=8 + // + // Find the greatest product of five consecutive digits in the 1000-digit number. + // + + static const char num[] = + "73167176531330624919225119674426574742355349194934" + "96983520312774506326239578318016984801869478851843" + "85861560789112949495459501737958331952853208805511" + "12540698747158523863050715693290963295227443043557" + "66896648950445244523161731856403098711121722383113" + "62229893423380308135336276614282806444486645238749" + "30358907296290491560440772390713810515859307960866" + "70172427121883998797908792274921901699720888093776" + "65727333001053367881220235421809751254540594752243" + "52584907711670556013604839586446706324415722155397" + "53697817977846174064955149290862569321978468622482" + "83972241375657056057490261407972968652414535100474" + "82166370484403199890008895243450658541227588666881" + "16427171479924442928230863465674813919123162824586" + "17866458359124566529476545682848912883142607690042" + "24219022671055626321111109370544217506941658960408" + "07198403850962455444362981230987879927244284909188" + "84580156166097919133875499200524063689912560717606" + "05886116467109405077541002256983155200055935729725" + "71636269561882670428252483600823257530420752963450"; + + auto task = [&](int n){ + for (int i = 0; i < n; ++i) { + auto range1 = int_range(0, _countof(num)-5); // 5 digit numbers, plus null terminator + + auto products = from(range1) + .select([&](int i){ return num+i;}) + .where([&](const char* s){ return !from(s, s+5).contains('0'); }) + .select([&](const char* s) { return from(s, s+5).select([](char c){ return c - '0'; }) + .aggregate(std::multiplies<int>()); }); + + auto result = products.max(); + if (n == 1) { + cout << "result = " << result << endl; + } + } + }; + cout << "length of input: " << (_countof(num)-1) << endl; + + task(1); + cout << endl; + +#ifdef PERF + test_perf(task); + cout << endl; +#endif +} + +int main(int argc, char** argv) +{ + size_t pass=0, fail=0; + testrange<0,__LINE__>().run(pass, fail); + + + cout << "pass: " << pass << ", fail: " << fail << endl; + if (fail){ + cerr << "ERRORS PRESENT." << endl; + } else if (!pass) { + cerr << "ERROR, no tests run" << endl; + } +} |