#include <iostream>
#include <vector>
#include <list>
#include <cstring>
#include <set>

#include <boost/iterator/filter_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/tuple/tuple.hpp>
#include <algorithm>
#include <memory>

template<typename IterT>
class iterator_stream {
private:
	IterT begin_, end_;
	
public:
	iterator_stream(IterT begin, IterT end) : begin_(std::move(begin)), end_(std::move(end)) { }
	
	IterT begin() const {
		return begin_;
	}
	
	IterT end() const {
		return end_;
	}
	
	template<typename CallbackT> void
	for_each(CallbackT cb) {
		std::for_each(begin_, end_, cb);
	}
	
	template<typename PredicateT>
	iterator_stream<boost::filter_iterator<PredicateT, IterT>> filter(PredicateT predicate) {
		typedef boost::filter_iterator<PredicateT, IterT> FilterIterT;
		return iterator_stream<FilterIterT>(FilterIterT(predicate, begin_, end_), FilterIterT(predicate, end_, end_));
	}
	
	template<typename MapperT>
	iterator_stream<boost::transform_iterator<MapperT, IterT>> map(MapperT mapper) {
		typedef boost::transform_iterator<MapperT, IterT> MapIterT;
		return iterator_stream<MapIterT>(MapIterT(begin_, mapper), MapIterT(end_, mapper));
	}
	
};

template<typename ContainerT>
auto stream(ContainerT& c) -> iterator_stream<typename ContainerT::iterator> {
	return iterator_stream<typename ContainerT::iterator>(c.begin(), c.end());
}

template<typename T, std::size_t Size>
iterator_stream<T*> stream(T (&a)[Size]) {
	return iterator_stream<T*>(a, a + Size);
}

template<typename Iter1T, typename Iter2T>
iterator_stream<boost::zip_iterator<boost::tuple<Iter1T, Iter2T>>> zip(iterator_stream<Iter1T> const& s1, iterator_stream<Iter2T> const& s2) {
	typedef boost::zip_iterator<boost::tuple<Iter1T, Iter2T>> ZipIterT;
	return iterator_stream<ZipIterT>(
		ZipIterT(boost::make_tuple(s1.begin(), s2.begin())),
		ZipIterT(boost::make_tuple(s1.end(), s2.end())));
}

int main() {
	std::vector<char const*> vec = { "Hello", "World", "!"};
	stream(vec)
		.filter([](auto& str) { return std::strlen(str) > 1; })
		.map([](auto& str) { return std::strlen(str); })
		.for_each([](auto len) { std::cout << len << std::endl; });
		
	std::list<std::string> en = { "Hello", "World", "Asshole" };
	char const* de[] = { "Hallo", "Welt", "Arschloch" };
	std::set<std::string> insults = { "Asshole" };
	zip(stream(en), stream(de))
		.filter([&] (auto const& t) { return insults.find(boost::get<0>(t)) == insults.end(); })
		.map([](auto const& t) { return boost::get<0>(t) + " - " + boost::get<1>(t); })
		.for_each([] (auto const& str) { std::cout << str << std::endl; });
}