summaryrefslogtreecommitdiff
path: root/Ix/CPP/unittest/testbench.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Ix/CPP/unittest/testbench.cpp')
-rw-r--r--Ix/CPP/unittest/testbench.cpp527
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;
+ }
+}