#include <iostream>
#include <cassert>
#include <string>
using namespace std;

auto identity = [](auto&& a) { return std::forward<decltype(a)>(a); };

auto compose = [](auto&& f, auto&& g) {
	return [f, g](auto&& val) { return f(g(std::forward<decltype(val)>(val))); };
};
	
template<typename F, typename G>
auto operator*(F&& f, G&& g) -> decltype(compose(f, g)) {
	return compose(std::forward<F>(f), std::forward<G>(g));
}

int main() {
	//identity
	assert(5 == identity(5));
	assert("asdf"s == identity("asdf"s));
	std::string aa = "adsf";
	auto new_aa = identity(std::move(aa));
	assert(new_aa == "adsf");
	assert(aa != "adsf");
	
	//composition
	auto intTimes10f = [](int a) { return a * 10.f; };
	auto floatTimes10s = [](float a) { return std::to_string(a * 10) + " string!"; };
	auto intToFloatToStringComposition = compose(floatTimes10s, intTimes10f);
	auto compositionByOperator = floatTimes10s * intTimes10f;
	std::cout << intToFloatToStringComposition(2);
	std::cout << "\nBy operator: " << compositionByOperator(2);
	
	
	//composition and identity
	auto idInComposition = compositionByOperator * identity;
	std::cout << "\nidentity + last composition yields the same value: " << idInComposition(2); //first id is applied, which yields 2 then previous composition works on this value
	
	auto reverse = identity * compositionByOperator;
	std::cout << "\nnow identity is applied after eval of previous composition: " << reverse(2); //first composition is applied which yields std::string, then id which yields the same std::string

	return 0;
}