/// lambda。

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/loops.hpp>
#include <boost/lambda/switch.hpp>
#include <boost/lambda/exceptions.hpp>
#include <boost/lambda/construct.hpp>
// gcc-3.0.4 に怒られました。
// #include <boost/lambda/algorithm.hpp>

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <stdexcept>

// まあエラーが見たければ外して下さい。
// #define WATCH_ERROR

int plus_one(int x) { return x+1; }

int plus_another(int x, int y) { return x+y; }

int one() { return 1; }

void throw_func(int x) {
	if (x == 8) throw std::runtime_error("8が来たから");
	else if (x == 12) throw std::exception();
}

class Class {
public:
	explicit Class(int id) : id_(id) {}

	void func0() const {
		std::cout << "func0: " << id_ << std::endl;
	}
	void func1(const Class& c1) const {
		std::cout << "func1: " << id_ << "(" << c1.id_ << ")" << std::endl;
	}
	void func2(const Class& c1, const Class& c2) const {
		std::cout << "func2: " << id_ << "("
				  << c1.id_ << ", " << c2.id_ << ")" << std::endl;
	}
	void func3(const Class& c1, const Class& c2, const Class& c3) const {
		std::cout << "func3: " << id_ << "(" << c1.id_ << ", "
				  << c2.id_ << ", " << c3.id_ << ")" << std::endl;
	}

	void funcNoRef(int i) const {
		std::cout << "funcNoRef: " << id_ << "(" << i << ")" << std::endl;
	}

	int id_;
};

int main() {
	using namespace boost::lambda;

	std::vector<int> is;
	is.resize(3);

	// 取り敢えず代入
	std::for_each(is.begin(), is.end(), _1 = 1);

	// 引数無しなら OK の模様
	std::for_each(is.begin(), is.end(), _1 = one());

	is.clear();
	is.push_back(0);
	is.push_back(1);
	is.push_back(2);

	// 関数呼び出しは普通にはできない
	std::for_each(is.begin(), is.end(), _1 = bind(plus_one, _1));
	std::for_each(is.begin(), is.end(), _1 = bind(plus_another, _1, _1));

	// ちなみにメソッドも bind 可。
	Class c1(1), c2(2);
	(bind(&Class::func1, c1, _1))(c2);

	// 関数オブジェクトは無理なのか。
    std::for_each(is.begin(), is.end(),
				  _1 = bind(std_functor(std::plus<int>()), _1, _1));

	// 二引数も OK です
	std::sort(is.begin(), is.end(), _1 < _2);

	// return タイプが決まらない場合はこうするらしい
	// どういう場合に決まらないかは説明が難しいんで省略。
	std::sort(is.begin(), is.end(), ret<bool>(_1 < _2));

	// これは良いが、
	std::for_each(is.begin(), is.end(), std::cout << _1 << ' ');

	// 定数が間に入る場合はこうしてくれとのこと
	std::for_each(is.begin(), is.end(), std::cout << constant(' ') << _1);

	// ここまでで "4 8 12\n"
	std::cout << std::endl;

	// 外部の変数使ってみようか。
	int index = 0;
#ifdef WATCH_ERROR
	std::for_each(is.begin(), is.end(),
				  std::cout << ++index << ':' << _1 << '\n');
#else
	// これ、gcc-2.96 では通りません、gcc-3.0.4 なら OK でした。
	// ここから先は gcc-3.0.4 でしか確認してません。
	std::for_each(is.begin(), is.end(),
				  std::cout << ++var(index) << ':' << _1 << '\n');
#endif

	// 条件分岐やってみよう。
	std::for_each(is.begin(), is.end(),
				  if_then(_1 % 3 == 0,
						  (std::cout << _1 << " : これだけ3の倍数\n")));

	std::for_each(is.begin(), is.end(),
				  if_(_1 % 3 == 0)[std::cout << _1 << " : 3 の倍数\n"].
				  else_[std::cout << _1 << " : 3 の倍数じゃない\n"]);

	// ループ
	int i;
	std::for_each(is.begin(), is.end(),
				  for_loop(var(i) = 0, var(i) < _1, ++var(i),
						   (std::cout << var(i) << ' ')));
	std::cout << std::endl;

	// while もね、do while はめんどいから省略するけど
	int x = 0;
	std::for_each(is.begin(), is.end(),
				  while_((var(x)+=_1) < _1 * 10)[std::cout << var(x) << ' ']);
	std::cout << std::endl;

	// switch
	std::for_each(is.begin(), is.end(),
				  (
					  switch_statement(
						  _1,
						  case_statement<4>(std::cout << constant("四")),
						  case_statement<8>(std::cout << constant("八")),
						  case_statement<12>(std::cout << constant("十二"))
						  ),
					  std::cout << constant("\n")
					  )
		);

	// 例外
	std::for_each(is.begin(), is.end(),
				  try_catch(
					  bind(throw_func, _1),
					  catch_exception<std::runtime_error>(
						  std::cout
						  << _1
						  << constant(" : runtime が出た理由は、")
						  << bind(&std::exception::what, _e) << '\n'
						  ),
					  catch_all(
						  std::cout
						  << _1
						  << constant(" : それ以外は 12\n")
						  )
					  )
		);

	// new, delete、めんどいからコピペ
	// コンストラクタ、デストラクタも似たようなもん。
	int* a[10];
	std::for_each(a, a+10, _1 = bind(new_ptr<int>()));
	std::for_each(a, a+10, bind(delete_ptr(), _1));

	// cast, sizeof, typeid も使えるよ、良かったね。省略。

	// やっと終わり、STL algorithm のネスト。コピペ。
#if 0
	// でもこれ、gcc-2.96, gcc-3.0.4, gcc-3.1, gcc-3.2 のどれでもダメでしたが。
	int b[100][200];
	int sum = 0;
	std::for_each(b, b + 100,
				  bind(ll::for_each(), _1, _1 + 200, protect(sum += _1)));
#endif

}


