#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

#include <experimental/coroutine>

template<typename T>
struct cor_range {
	struct promise_type {
		T _value;

		cor_range get_return_object() {
			return cor_range(std::experimental::coroutine_handle<promise_type>::from_promise(*this));
		}

		auto initial_suspend() { return std::experimental::suspend_always(); }
		auto final_suspend()   { return std::experimental::suspend_always(); }

		void return_value(T value) {
			_value = value;
		}

		auto yield_value(T value) {
			_value = value;
			return std::experimental::suspend_always();
		}

		void unhandled_exception() {}
	};

	std::experimental::coroutine_handle<promise_type> _coroutine = nullptr;

	explicit cor_range(std::experimental::coroutine_handle<promise_type> coroutine)
		: _coroutine(coroutine)
	{}

	~cor_range() {
		if(_coroutine) { _coroutine.destroy(); }
	}

	cor_range() = default;
	cor_range(cor_range const &) = delete;
	cor_range& operator=(cor_range const &) = delete;

	cor_range(cor_range&& other) {
		std::swap(_coroutine, other._coroutine);
	}

	cor_range& operator=(cor_range&& other) {
		if(&other != this) {
			_coroutine = other._coroutine;
			other._coroutine = nullptr;
		}
		return *this;
	}

	T next() {
		_coroutine.resume();
		return _coroutine.promise()._value;
	}

	struct iterator : std::iterator<std::input_iterator_tag, T> {
		std::experimental::coroutine_handle<promise_type> _coroutine = nullptr;

		iterator() = default;
		explicit iterator(std::experimental::coroutine_handle<promise_type> coroutine)
			: _coroutine(coroutine)
		{}

		iterator& operator++() {
			_coroutine.resume();
			return *this;
		}

		T const & operator*() const {
			return _coroutine.promise()._value;
		}
	};

	iterator begin() {
		if(_coroutine) {
			_coroutine.resume();
			if(_coroutine.done()) { return end(); }
		}

		return iterator{ _coroutine };
	}

	iterator end() { return iterator{}; }
};

cor_range<int> fib() {
	int fn1 = 0, fn = 1;
	for(;;) {
		co_yield fn1;
		int temp = fn1;
		fn1 = fn;
		fn = temp + fn;
	}
}

int main() {
	{
		auto f1 = fib();
		auto f2 = fib();
		for(int i = 0; i < 10; i++) {
			std::cout << f1.next() << " " << f2.next() << std::endl;
		}
	}

	{
		auto f1 = fib();
		std::vector<int> fibs;
		std::copy_n(f1.begin(), 10, std::back_inserter(fibs));

		for(auto i : fibs) {
			std::cout << i << std::endl;
		}
	}
}
