#if _MSC_VER
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
#include <intrin.h>
#else
#include <x86intrin.h>
#endif
#include <iostream>
#include <stdio.h> // printf
#include <string.h> // memcmp
#define min_( a, b ) ( (a) < (b) ? (a) : (b) )
//------------------------------+---------------------------------------------------------------
#ifndef SYS_BITS
#define CHAR_BITS 8
#define SYS_BYTES sizeof( std::size_t )
#define SYS_BITS ( SYS_BYTES * CHAR_BITS )
#endif
//------------------------------+---------------------------------------------------------------
using ON_RUN = void( & )( void* );
using ON_PREPARE = void( & )( void* );
using ON_COMPARE = bool( & )( void*, void*, std::size_t );
//------------------------------+---------------------------------------------------------------
// BENCH 에 의해서만 접근되기 때문에 모든 멤버가 private ( class default ) 입니다
class RUNNER
{
friend class BENCH;
const char* name;
unsigned long long elapsed;
const ON_RUN on_run;
void* result;
RUNNER( const char* name, ON_RUN&& on_run, void* result )
: name( name )
, on_run( on_run )
, elapsed( -1 )
, result( result )
{
}
void run()
{
auto begin = __rdtsc();
on_run( result );
elapsed = min_( elapsed, __rdtsc() - begin );
}
};
///-----------------------------+---------------------------------------------------------------
#include <vector>
class BENCH
{
private:
const char* title;
std::vector< RUNNER* > runners;
ON_PREPARE on_prepare;
ON_COMPARE on_compare;
void* answer;
std::size_t answer_size;
const unsigned int trial;
public:
BENCH( const char* title, const int trial,
ON_PREPARE& prepare, ON_COMPARE& compare
)
: title( title )
, trial( trial )
, on_prepare( prepare )
, on_compare( compare )
{
};
~BENCH()
{
for( auto runner : runners )
delete[] runner;
runners.clear();
}
auto record( unsigned int index ) const
{
return runners[ index ]->elapsed;
}
auto runner_count() const
{
return runners.size();
}
void solution( ON_RUN& correct_function,
void* result, const std::size_t bytes )
{
answer = result;
answer_size = bytes;
on_prepare( result );
correct_function( result );
}
template< typename T >
void solution( ON_RUN& correct_function,
T& result, const std::size_t bytes )
{
solution( correct_function, &result, bytes );
}
void add( const char* name, ON_RUN& on_run, void* result )
{
runners.emplace_back( new RUNNER( name, on_run, result ) );
}
template< typename T >
void add( const char* name, ON_RUN& on_run, T& result )
{
add( name, on_run, &result );
}
void run() const
{
if( runners.empty() )
return;
printf( "\n < %d bits %d trial > %s\n", (int)SYS_BITS, trial, title );
puts( " ----------------+---------------------------------+-----------------------" );
puts( " | CHECKER | function name | minimum clocks |" );
puts( " ----------------+---------------------------------+-----------------------" );
unsigned long long min_clocks = -1;
unsigned long long max_clocks = 0;
RUNNER* min_runner = runners[ 0 ];
RUNNER* max_runner = runners[ 0 ];
for( const auto runner : runners )
{
int pass_count = 0;
for( unsigned int i = 0; i < trial; ++i )
{
on_prepare( runner->result );
runner->run();
pass_count += on_compare( runner->result, answer, answer_size );
}
if( min_clocks > runner->elapsed )
{
min_runner = runner;
min_clocks = runner->elapsed;
}
if( max_clocks < runner->elapsed )
{
max_runner = runner;
max_clocks = runner->elapsed;
}
char temp[ 14 ];
if( trial == pass_count )
sprintf( temp, " PASSED" );
else
sprintf( temp, "FAILED%7d", trial - pass_count );
printf( " [ %s ] %32s %15llu clocks\n", temp, runner->name, runner->elapsed );
}
puts( " --------------------------------------------------------------------------" );
printf( " Winner is %s ( %.2f times faster )\n\n",
min_runner->name, float( max_clocks ) / min_clocks );
}
};
#define FUN( function_name ) #function_name, function_name
//==============================================================================================
//------------------------------+---------------------------------------------------------------
using SOURCE = char;
using RESULT = char;
SOURCE src1[ 256 ];
SOURCE src2[ 256 ];
constexpr std::size_t SIZE = sizeof src1 / sizeof *src1;
RESULT dst0[ SIZE ], dst1[ SIZE ], dst2[ SIZE ], dst3[ SIZE ], dst4[ SIZE ],
dst5[ SIZE ], dst6[ SIZE ], dst7[ SIZE ], dst8[ SIZE ], dst9[ SIZE ];
//------------------------------+---------------------------------------------------------------
// 테스트전에 불려질 초기화 함수
void prepare( void* result )
{
strcpy( src1, "___#*U#U*$@*#UU@TDKDAJKFKDKJFFJKbcdefghijklmnopqrstuvwxyz");
strcpy( src2, "___#*U#U*$@*#UU@TDKDJKFKDKJFFJKbcdefghijklmnopqrstuvwxyz");
// memcpy( result, src, sizeof src );
}
// 테스트후에 불려질 점검 함수
bool compare( void* result, void* answer, std::size_t bytes )
{
return memcmp( result, answer, bytes ) == 0;
}
//------------------------------+---------------------------------------------------------------
// 테스트할 함수들
//------------------------------+---------------------------------------------------------------
namespace hsoo
{
int is_pangram(const char *str)
{
int i = *str;
int j = 1;
for( ; i < 10000; ++i ) j+=i;
return j;
}
void run( void* dst )
{
bool* b = (bool*)dst;
b[0] = is_pangram( src1 );
b[1] = is_pangram( src2 );
}
}
//------------------------------+---------------------------------------------------------------
namespace cose
{
const unsigned int alphaset[ 128 ] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
1 << 7, 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14,
1 << 15, 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 21, 1 << 22,
1 << 23, 1 << 24, 1 << 25, 0, 0, 0, 0, 0,
0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
1 << 7, 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14,
1 << 15, 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 21, 1 << 22,
1 << 23, 1 << 24, 1 << 25, 0, 0, 0, 0, 0,
};
bool is_pangram( char* str )
{
unsigned int mux = 0;
while( *str ) mux |= alphaset[ *str++ ];
return mux == 0x03FFFFFF;
}
void run( void* dst )
{
bool* b = (bool*)dst;
b[0] = is_pangram( src1 );
b[1] = is_pangram( src2 );
}
}
//------------------------------+---------------------------------------------------------------
#include <iostream>
using namespace std;
int main()
{
BENCH bench( "pangram", 1000000, prepare, compare );
bench.solution( cose::run, dst0, sizeof dst0 );
bench.add( FUN( cose::run ), dst1 );
bench.add( FUN( hsoo::run ), dst8 );
/*
bench.add( FUN( adun::run ), dst2 );
bench.add( FUN( asdf::run ), dst3 );
bench.add( FUN( powerup::run ), dst4 );
bench.add( FUN( oo_203_84::run ), dst5 );
bench.add( FUN( f::run ), dst6 );
bench.add( FUN( wizard::run ), dst7 );
*/
bench.run();
getchar();
return 0;
}
I2lmIF9NU0NfVkVSCiNwcmFnbWEgd2FybmluZyhkaXNhYmxlOiA0OTk2KSAgLy8gX0NSVF9TRUNVUkVfTk9fV0FSTklOR1MKI2luY2x1ZGUgPGludHJpbi5oPgojZWxzZQojaW5jbHVkZSA8eDg2aW50cmluLmg+CiNlbmRpZgoKI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RkaW8uaD4gICAgICAgICAgICAgIC8vIHByaW50ZgojaW5jbHVkZSA8c3RyaW5nLmg+ICAgICAgICAgICAgIC8vIG1lbWNtcAojZGVmaW5lIG1pbl8oIGEsIGIgKSAgICAgICAgICAgICggKGEpIDwgKGIpID8gKGEpIDogKGIpICkKCi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKI2lmbmRlZiBTWVNfQklUUwojZGVmaW5lIENIQVJfQklUUyAgICAgICAgICAgICAgIDgKI2RlZmluZSBTWVNfQllURVMgICAgICAgICAgICAgICBzaXplb2YoIHN0ZDo6c2l6ZV90ICkKI2RlZmluZSBTWVNfQklUUyAgICAgICAgICAgICAgICAoIFNZU19CWVRFUyAqIENIQVJfQklUUyApCiNlbmRpZgoKLy8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgp1c2luZyAgIE9OX1JVTiAgICAgID0gICAgICAgICAgIHZvaWQoICYgKSggdm9pZCogKTsKdXNpbmcgICBPTl9QUkVQQVJFICA9ICAgICAgICAgICB2b2lkKCAmICkoIHZvaWQqICk7CnVzaW5nICAgT05fQ09NUEFSRSAgPSAgICAgICAgICAgYm9vbCggJiApKCB2b2lkKiwgdm9pZCosIHN0ZDo6c2l6ZV90ICk7CgovLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gIEJFTkNIIOyXkCDsnZjtlbTshJzrp4wg7KCR6re865CY6riwIOuVjOusuOyXkCDrqqjrk6Ag66mk67KE6rCAIHByaXZhdGUgKCBjbGFzcyBkZWZhdWx0ICkg7J6F64uI64ukCmNsYXNzICAgICAgICAgICAgICAgICAgICAgICAgICAgUlVOTkVSCnsKZnJpZW5kICBjbGFzcyAgICAgICAgICAgICAgICAgICBCRU5DSDsKICAgIGNvbnN0ICAgY2hhciogICAgICAgICAgICAgICBuYW1lOwogICAgdW5zaWduZWQgbG9uZyBsb25nICAgICAgICAgIGVsYXBzZWQ7CiAgICBjb25zdCAgIE9OX1JVTiAgICAgICAgICAgICAgb25fcnVuOwogICAgdm9pZCogICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdDsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUlVOTkVSKCBjb25zdCBjaGFyKiBuYW1lLCBPTl9SVU4mJiBvbl9ydW4sIHZvaWQqIHJlc3VsdCApCiAgICA6ICAgbmFtZSggbmFtZSApCiAgICAsICAgb25fcnVuKCBvbl9ydW4gKQogICAgLCAgIGVsYXBzZWQoIC0xICkKICAgICwgICByZXN1bHQoIHJlc3VsdCApCiAgICB7CiAgICB9CgogICAgdm9pZCAgICAgICAgICAgICAgICAgICAgICAgIHJ1bigpCiAgICB7CiAgICAgICAgYXV0byAgICBiZWdpbiAgID0gX19yZHRzYygpOwogICAgICAgIG9uX3J1biggcmVzdWx0ICk7CiAgICAgICAgZWxhcHNlZCAgICAgICAgID0gbWluXyggZWxhcHNlZCwgX19yZHRzYygpIC0gYmVnaW4gKTsKICAgIH0KfTsKLy8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojaW5jbHVkZSA8dmVjdG9yPgoKY2xhc3MgICAgICAgICAgICAgICAgICAgICAgICAgICBCRU5DSAp7CnByaXZhdGU6CiAgICBjb25zdCAgIGNoYXIqICAgICAgICAgICAgICAgdGl0bGU7CiAgICBzdGQ6OnZlY3RvcjwgUlVOTkVSKiA+ICAgICAgcnVubmVyczsKICAgIE9OX1BSRVBBUkUgICAgICAgICAgICAgICAgICBvbl9wcmVwYXJlOwogICAgT05fQ09NUEFSRSAgICAgICAgICAgICAgICAgIG9uX2NvbXBhcmU7CiAgICB2b2lkKiAgICAgICAgICAgICAgICAgICAgICAgYW5zd2VyOwogICAgc3RkOjpzaXplX3QgICAgICAgICAgICAgICAgIGFuc3dlcl9zaXplOwogICAgY29uc3QgICB1bnNpZ25lZCBpbnQgICAgICAgIHRyaWFsOwoKcHVibGljOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJFTkNIKCBjb25zdCBjaGFyKiB0aXRsZSwgY29uc3QgaW50IHRyaWFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPTl9QUkVQQVJFJiBwcmVwYXJlLCBPTl9DT01QQVJFJiBjb21wYXJlCiAgICApCiAgICA6ICAgdGl0bGUoIHRpdGxlICkKICAgICwgICB0cmlhbCggdHJpYWwgKQogICAgLCAgIG9uX3ByZXBhcmUoIHByZXBhcmUgKQogICAgLCAgIG9uX2NvbXBhcmUoIGNvbXBhcmUgKQogICAgewogICAgfTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH5CRU5DSCgpCiAgICB7CiAgICAgICAgZm9yKCBhdXRvIHJ1bm5lciA6IHJ1bm5lcnMgKQogICAgICAgICAgICBkZWxldGVbXSBydW5uZXI7CiAgICAgICAgcnVubmVycy5jbGVhcigpOwogICAgfQoKICAgIGF1dG8gICAgICAgICAgICAgICAgICAgICAgICByZWNvcmQoIHVuc2lnbmVkIGludCBpbmRleCApIGNvbnN0CiAgICB7CiAgICAgICAgcmV0dXJuICBydW5uZXJzWyBpbmRleCBdLT5lbGFwc2VkOwogICAgfQoKICAgIGF1dG8gICAgICAgICAgICAgICAgICAgICAgICBydW5uZXJfY291bnQoKSBjb25zdAogICAgewogICAgICAgIHJldHVybiAgcnVubmVycy5zaXplKCk7CiAgICB9CgogICAgdm9pZCAgICAgICAgICAgICAgICAgICAgICAgIHNvbHV0aW9uKCBPTl9SVU4mIGNvcnJlY3RfZnVuY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZvaWQqIHJlc3VsdCwgY29uc3Qgc3RkOjpzaXplX3QgYnl0ZXMgKQogICAgewogICAgICAgIGFuc3dlciAgICAgID0gcmVzdWx0OwogICAgICAgIGFuc3dlcl9zaXplID0gYnl0ZXM7CiAgICAgICAgb25fcHJlcGFyZSggcmVzdWx0ICk7CiAgICAgICAgY29ycmVjdF9mdW5jdGlvbiggcmVzdWx0ICk7CiAgICB9CgogICAgdGVtcGxhdGU8IHR5cGVuYW1lIFQgPgogICAgdm9pZCAgICAgICAgICAgICAgICAgICAgICAgIHNvbHV0aW9uKCBPTl9SVU4mIGNvcnJlY3RfZnVuY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFQmIHJlc3VsdCwgY29uc3Qgc3RkOjpzaXplX3QgYnl0ZXMgKQogICAgewogICAgICAgIHNvbHV0aW9uKCBjb3JyZWN0X2Z1bmN0aW9uLCAmcmVzdWx0LCBieXRlcyApOwogICAgfQoKICAgIHZvaWQgICAgICAgICAgICAgICAgICAgICAgICBhZGQoIGNvbnN0IGNoYXIqIG5hbWUsIE9OX1JVTiYgb25fcnVuLCB2b2lkKiByZXN1bHQgKQogICAgewogICAgICAgIHJ1bm5lcnMuZW1wbGFjZV9iYWNrKCBuZXcgUlVOTkVSKCBuYW1lLCBvbl9ydW4sIHJlc3VsdCApICk7CiAgICB9CgogICAgdGVtcGxhdGU8IHR5cGVuYW1lIFQgPgogICAgdm9pZCAgICAgICAgICAgICAgICAgICAgICAgIGFkZCggY29uc3QgY2hhciogbmFtZSwgT05fUlVOJiBvbl9ydW4sIFQmIHJlc3VsdCApCiAgICB7CiAgICAgICAgYWRkKCBuYW1lLCBvbl9ydW4sICZyZXN1bHQgKTsKICAgIH0KCiAgICB2b2lkICAgICAgICAgICAgICAgICAgICAgICAgcnVuKCkgY29uc3QKICAgIHsKICAgICAgICBpZiggcnVubmVycy5lbXB0eSgpICkKICAgICAgICAgICAgcmV0dXJuOwoKICAgICAgICBwcmludGYoICJcbiAgIDwgJWQgYml0cyAlZCB0cmlhbCA+ICAgICVzXG4iLCAoaW50KVNZU19CSVRTLCB0cmlhbCwgdGl0bGUgKTsKICAgICAgICBwdXRzKCAiICAgLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0iICk7CiAgICAgICAgcHV0cyggIiAgIHwgICAgQ0hFQ0tFUiAgICB8ICAgICAgICAgIGZ1bmN0aW9uIG5hbWUgICAgICAgICAgfCAgICBtaW5pbXVtIGNsb2NrcyAgICB8IiApOwogICAgICAgIHB1dHMoICIgICAtLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSIgKTsKCiAgICAgICAgdW5zaWduZWQgbG9uZyBsb25nICBtaW5fY2xvY2tzID0gLTE7CiAgICAgICAgdW5zaWduZWQgbG9uZyBsb25nICBtYXhfY2xvY2tzID0gMDsKICAgICAgICBSVU5ORVIqIG1pbl9ydW5uZXIgPSBydW5uZXJzWyAwIF07CiAgICAgICAgUlVOTkVSKiBtYXhfcnVubmVyID0gcnVubmVyc1sgMCBdOwoKICAgICAgICBmb3IoIGNvbnN0ICBhdXRvICAgIHJ1bm5lciA6IHJ1bm5lcnMgKQogICAgICAgIHsKICAgICAgICAgICAgaW50ICAgICBwYXNzX2NvdW50ID0gMDsKICAgICAgICAgICAgZm9yKCB1bnNpZ25lZCBpbnQgaSA9IDA7IGkgPCB0cmlhbDsgKytpICkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgb25fcHJlcGFyZSggcnVubmVyLT5yZXN1bHQgKTsKICAgICAgICAgICAgICAgIHJ1bm5lci0+cnVuKCk7CiAgICAgICAgICAgICAgICBwYXNzX2NvdW50ICs9IG9uX2NvbXBhcmUoIHJ1bm5lci0+cmVzdWx0LCBhbnN3ZXIsIGFuc3dlcl9zaXplICk7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGlmKCBtaW5fY2xvY2tzID4gcnVubmVyLT5lbGFwc2VkICkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgbWluX3J1bm5lciA9IHJ1bm5lcjsKICAgICAgICAgICAgICAgIG1pbl9jbG9ja3MgPSBydW5uZXItPmVsYXBzZWQ7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYoIG1heF9jbG9ja3MgPCBydW5uZXItPmVsYXBzZWQgKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBtYXhfcnVubmVyID0gcnVubmVyOwogICAgICAgICAgICAgICAgbWF4X2Nsb2NrcyA9IHJ1bm5lci0+ZWxhcHNlZDsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgY2hhciAgICB0ZW1wWyAxNCBdOwogICAgICAgICAgICBpZiggdHJpYWwgPT0gcGFzc19jb3VudCApCiAgICAgICAgICAgICAgICBzcHJpbnRmKCB0ZW1wLCAiICAgICAgIFBBU1NFRCIgKTsKICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgc3ByaW50ZiggdGVtcCwgIkZBSUxFRCU3ZCIsIHRyaWFsIC0gcGFzc19jb3VudCApOwoKICAgICAgICAgICAgcHJpbnRmKCAiICAgWyAlcyBdICUzMnMgJTE1bGx1IGNsb2Nrc1xuIiwgdGVtcCwgcnVubmVyLT5uYW1lLCBydW5uZXItPmVsYXBzZWQgKTsKICAgICAgICB9CiAgICAgICAgcHV0cyggIiAgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIiApOwogICAgICAgIHByaW50ZiggIiAgIFdpbm5lciBpcyAlcyAgKCAlLjJmIHRpbWVzIGZhc3RlciApXG5cbiIsCiAgICAgICAgICAgICAgICBtaW5fcnVubmVyLT5uYW1lLCBmbG9hdCggbWF4X2Nsb2NrcyApIC8gbWluX2Nsb2NrcyApOwogICAgfQp9OwoKI2RlZmluZSBGVU4oIGZ1bmN0aW9uX25hbWUgKSAgICAjZnVuY3Rpb25fbmFtZSwgZnVuY3Rpb25fbmFtZQoKLy89PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ci8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKdXNpbmcgICBTT1VSQ0UgPSBjaGFyOwp1c2luZyAgIFJFU1VMVCA9IGNoYXI7CgpTT1VSQ0UgIHNyYzFbIDI1NiBdOwpTT1VSQ0UgIHNyYzJbIDI1NiBdOwoKY29uc3RleHByICAgc3RkOjpzaXplX3QgU0laRSA9IHNpemVvZiBzcmMxIC8gc2l6ZW9mICpzcmMxOwoKUkVTVUxUICBkc3QwWyBTSVpFIF0sIGRzdDFbIFNJWkUgXSwgZHN0MlsgU0laRSBdLCBkc3QzWyBTSVpFIF0sIGRzdDRbIFNJWkUgXSwKICAgICAgICBkc3Q1WyBTSVpFIF0sIGRzdDZbIFNJWkUgXSwgZHN0N1sgU0laRSBdLCBkc3Q4WyBTSVpFIF0sIGRzdDlbIFNJWkUgXTsKCi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyAg7YWM7Iqk7Yq47KCE7JeQIOu2iOugpOyniCDstIjquLDtmZQg7ZWo7IiYCnZvaWQgICAgcHJlcGFyZSggdm9pZCogcmVzdWx0ICkKewoJc3RyY3B5KCBzcmMxLCAiX19fIypVI1UqJEAqI1VVQFRES0RBSktGS0RLSkZGSktiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6Iik7CglzdHJjcHkoIHNyYzIsICJfX18jKlUjVSokQCojVVVAVERLREpLRktES0pGRkpLYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiIpOwovLyAgICBtZW1jcHkoIHJlc3VsdCwgc3JjLCBzaXplb2Ygc3JjICk7Cn0KLy8gIO2FjOyKpO2KuO2bhOyXkCDrtojroKTsp4gg7KCQ6rKAIO2VqOyImApib29sICAgIGNvbXBhcmUoIHZvaWQqIHJlc3VsdCwgdm9pZCogYW5zd2VyLCBzdGQ6OnNpemVfdCBieXRlcyApCnsKICAgIHJldHVybiAgbWVtY21wKCByZXN1bHQsIGFuc3dlciwgYnl0ZXMgKSA9PSAwOwp9Ci8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyAg7YWM7Iqk7Yq47ZWgIO2VqOyImOuTpAovLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCm5hbWVzcGFjZSBoc29vCnsKCQppbnQgaXNfcGFuZ3JhbShjb25zdCBjaGFyICpzdHIpCnsKCWludCBpID0gKnN0cjsKCWludCBqID0gMTsKCWZvciggOyBpIDwgMTAwMDA7ICsraSApIGorPWk7CiAgICByZXR1cm4gajsKfQoKCnZvaWQgICAgcnVuKCB2b2lkKiBkc3QgKQp7Cglib29sKiBiID0gKGJvb2wqKWRzdDsKCWJbMF0gPSBpc19wYW5ncmFtKCBzcmMxICk7CgliWzFdID0gaXNfcGFuZ3JhbSggc3JjMiApOwp9Cgp9Ci8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKbmFtZXNwYWNlIGNvc2UKewoKY29uc3QgdW5zaWduZWQgaW50IGFscGhhc2V0WyAxMjggXSA9CnsKICAgIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsCiAgICAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLAogICAgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwKICAgIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsCiAgICAwLCAxIDw8IDAsIDEgPDwgMSwgMSA8PCAyLCAxIDw8IDMsIDEgPDwgNCwgMSA8PCA1LCAxIDw8IDYsCiAgICAxIDw8IDcsIDEgPDwgOCwgMSA8PCA5LCAxIDw8IDEwLCAxIDw8IDExLCAxIDw8IDEyLCAxIDw8IDEzLCAxIDw8IDE0LAogICAgMSA8PCAxNSwgMSA8PCAxNiwgMSA8PCAxNywgMSA8PCAxOCwgMSA8PCAxOSwgMSA8PCAyMCwgMSA8PCAyMSwgMSA8PCAyMiwKICAgIDEgPDwgMjMsIDEgPDwgMjQsIDEgPDwgMjUsIDAsIDAsIDAsIDAsIDAsCiAgICAwLCAxIDw8IDAsIDEgPDwgMSwgMSA8PCAyLCAxIDw8IDMsIDEgPDwgNCwgMSA8PCA1LCAxIDw8IDYsCiAgICAxIDw8IDcsIDEgPDwgOCwgMSA8PCA5LCAxIDw8IDEwLCAxIDw8IDExLCAxIDw8IDEyLCAxIDw8IDEzLCAxIDw8IDE0LAogICAgMSA8PCAxNSwgMSA8PCAxNiwgMSA8PCAxNywgMSA8PCAxOCwgMSA8PCAxOSwgMSA8PCAyMCwgMSA8PCAyMSwgMSA8PCAyMiwKICAgIDEgPDwgMjMsIDEgPDwgMjQsIDEgPDwgMjUsIDAsIDAsIDAsIDAsIDAsCn07Cgpib29sIGlzX3BhbmdyYW0oIGNoYXIqIHN0ciApCnsKICAgIHVuc2lnbmVkIGludCBtdXggPSAwOwogICAgd2hpbGUoICpzdHIgKSBtdXggfD0gYWxwaGFzZXRbICpzdHIrKyBdOwogICAgcmV0dXJuIG11eCA9PSAweDAzRkZGRkZGOwp9Cgp2b2lkICAgIHJ1biggdm9pZCogZHN0ICkKewoJYm9vbCogYiA9IChib29sKilkc3Q7CgliWzBdID0gaXNfcGFuZ3JhbSggc3JjMSApOwoJYlsxXSA9IGlzX3BhbmdyYW0oIHNyYzIgKTsKfQoKfQoKLy8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiNpbmNsdWRlIDxpb3N0cmVhbT4KCnVzaW5nIG5hbWVzcGFjZSBzdGQ7CgppbnQgbWFpbigpCnsKICAgIEJFTkNIIGJlbmNoKCAicGFuZ3JhbSIsIDEwMDAwMDAsIHByZXBhcmUsIGNvbXBhcmUgKTsKICAgIGJlbmNoLnNvbHV0aW9uKCBjb3NlOjpydW4sIGRzdDAsIHNpemVvZiBkc3QwICk7CiAgICBiZW5jaC5hZGQoIEZVTiggY29zZTo6cnVuICApLCBkc3QxICk7CiAgICBiZW5jaC5hZGQoIEZVTiggaHNvbzo6cnVuICksIGRzdDggKTsKLyoKICAgIGJlbmNoLmFkZCggRlVOKCBhZHVuOjpydW4gICksIGRzdDIgKTsKICAgIGJlbmNoLmFkZCggRlVOKCBhc2RmOjpydW4gICksIGRzdDMgKTsKICAgIGJlbmNoLmFkZCggRlVOKCBwb3dlcnVwOjpydW4gKSwgZHN0NCApOwogICAgYmVuY2guYWRkKCBGVU4oIG9vXzIwM184NDo6cnVuICksIGRzdDUgKTsKICAgIGJlbmNoLmFkZCggRlVOKCBmOjpydW4gKSwgZHN0NiApOwogICAgYmVuY2guYWRkKCBGVU4oIHdpemFyZDo6cnVuICksIGRzdDcgKTsKKi8KICAgIGJlbmNoLnJ1bigpOwoKICAgIGdldGNoYXIoKTsKICAgIHJldHVybiAgMDsKfQ==