#include <iostream>
struct fact {
template < typename F> unsigned operator( ) ( unsigned i, F&& f) {
if ( i == 0 )
return 1 ;
return i * f( i - 1 ) ;
}
} ;
struct doubler {
template < typename F> unsigned operator( ) ( unsigned i, F&& f) {
return 2 * f( i) ;
}
} ;
template < typename F> struct y_combinator_ref {
F& f;
y_combinator_ref( F& farg)
: f( farg) { }
template < typename ... Args > auto operator( ) ( Args&& ... args )
- > decltype( f( std:: forward < Args> ( args) ..., y_combinator_ref< F> ( f) ) ) {
return f( std:: forward < Args> ( args) ..., y_combinator_ref< F> ( f) ) ;
}
} ;
template < typename F> struct y_combinator {
y_combinator( F farg)
: f( std:: move ( farg) ) { }
F f;
template < typename ... Args > auto operator( ) ( Args&& ... args )
- > decltype( f( std:: forward < Args> ( args) ..., y_combinator_ref< F> ( f) ) ) {
return f( std:: forward < Args> ( args) ..., y_combinator_ref< F> ( f) ) ;
}
} ;
template < typename F> y_combinator< F> y_combine( F f) {
return y_combinator< F> ( std:: move ( f) ) ;
}
int main( ) {
auto result = y_combine( fact( ) ) ;
std:: cout << result( 5 ) ;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgoKc3RydWN0IGZhY3QgewogICAgdGVtcGxhdGU8dHlwZW5hbWUgRj4gdW5zaWduZWQgb3BlcmF0b3IoKSh1bnNpZ25lZCBpLCBGJiYgZikgewogICAgICAgIGlmKGkgPT0gMCkKICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgcmV0dXJuIGkgKiBmKGkgLSAxKTsKICAgIH0KfTsKc3RydWN0IGRvdWJsZXIgewogICAgdGVtcGxhdGU8dHlwZW5hbWUgRj4gdW5zaWduZWQgb3BlcmF0b3IoKSh1bnNpZ25lZCBpLCBGJiYgZikgewogICAgICAgIHJldHVybiAyICogZihpKTsKICAgIH0KfTsKdGVtcGxhdGU8dHlwZW5hbWUgRj4gc3RydWN0IHlfY29tYmluYXRvcl9yZWYgewogICAgRiYgZjsKICAgIHlfY29tYmluYXRvcl9yZWYoRiYgZmFyZykKICAgICAgICA6IGYoZmFyZykge30KICAgIHRlbXBsYXRlPHR5cGVuYW1lLi4uIEFyZ3M+IGF1dG8gb3BlcmF0b3IoKShBcmdzJiYuLi4gYXJncykgCiAgICAtPiBkZWNsdHlwZShmKHN0ZDo6Zm9yd2FyZDxBcmdzPihhcmdzKS4uLiwgeV9jb21iaW5hdG9yX3JlZjxGPihmKSkpIHsKICAgICAgICByZXR1cm4gZihzdGQ6OmZvcndhcmQ8QXJncz4oYXJncykuLi4sIHlfY29tYmluYXRvcl9yZWY8Rj4oZikpOwogICAgfQp9Owp0ZW1wbGF0ZTx0eXBlbmFtZSBGPiBzdHJ1Y3QgeV9jb21iaW5hdG9yIHsKICAgIHlfY29tYmluYXRvcihGIGZhcmcpIAogICAgICAgIDogZihzdGQ6Om1vdmUoZmFyZykpIHt9CiAgICBGIGY7CiAgICB0ZW1wbGF0ZTx0eXBlbmFtZS4uLiBBcmdzPiBhdXRvIG9wZXJhdG9yKCkoQXJncyYmLi4uIGFyZ3MpIAogICAgLT4gZGVjbHR5cGUoZihzdGQ6OmZvcndhcmQ8QXJncz4oYXJncykuLi4sIHlfY29tYmluYXRvcl9yZWY8Rj4oZikpKSB7CiAgICAgICAgcmV0dXJuIGYoc3RkOjpmb3J3YXJkPEFyZ3M+KGFyZ3MpLi4uLCB5X2NvbWJpbmF0b3JfcmVmPEY+KGYpKTsKICAgIH0KfTsKdGVtcGxhdGU8dHlwZW5hbWUgRj4geV9jb21iaW5hdG9yPEY+IHlfY29tYmluZShGIGYpIHsKICAgIHJldHVybiB5X2NvbWJpbmF0b3I8Rj4oc3RkOjptb3ZlKGYpKTsKfQppbnQgbWFpbigpIHsKICAgIGF1dG8gcmVzdWx0ID0geV9jb21iaW5lKGZhY3QoKSk7CiAgICBzdGQ6OmNvdXQgPDwgcmVzdWx0KDUpOwp9