#include <windows.h>
#include <stdio.h>
class StopWatch {
LONGLONG start_time,lap_time,freq;
public:
StopWatch() {
freq = 0;
}
~StopWatch() {
}
bool Setup() {
if (! QueryPerformanceFrequency((LARGE_INTEGER*)&freq)) {
fprintf(stderr, "このハードウェアはパフォーマンスカウンタをサポートしていない為、計測できません。\n");
return false;
}
return true;
}
void Start() {
QueryPerformanceCounter((LARGE_INTEGER*)&start_time);
}
double LapTime() {
QueryPerformanceCounter((LARGE_INTEGER*)&lap_time);
return (double)(lap_time-start_time)/(double)freq;
}
};
class CpuFreq {
StopWatch stopwatch;
double freq;
public:
CpuFreq() {
}
~CpuFreq() {
}
bool Setup() {
if (!stopwatch.Setup()) {
return false;
}
bool except_flag = false;
__try {
__asm {
rdtsc
}
} __except (true) {
except_flag = true;
}
if (except_flag) {
fprintf(stderr, "このCPUはrdtsc命令をサポートしていない為、計測できません。\n");
return false;
}
HANDLE process = GetCurrentProcess();
int i;
double maxf=0.0;
for (i=0; i<5; ++i) {
Sleep(10);
SetPriorityClass(process, REALTIME_PRIORITY_CLASS);
LONGLONG startcount, lapcount;
double laptime;
stopwatch.Start();
__asm {
rdtsc
mov dword ptr startcount, eax
mov dword ptr startcount+4, edx
}
while ((laptime=stopwatch.LapTime()) < 0.1);
__asm {
rdtsc
mov dword ptr lapcount, eax
mov dword ptr lapcount+4, edx
}
SetPriorityClass(process, NORMAL_PRIORITY_CLASS);
double f = (double)(lapcount-startcount) / laptime;
if (f > maxf) maxf = f;
}
freq = maxf;
return true;
}
double GetFreq() {
return freq;
}
};
#define MMX_FLAG 0x00800000
#define SSE_FLAG 0x02000000
#define SSE2_FLAG 0x04000000
#define BUFSIZE (32*1024*1024)
int buf[(BUFSIZE+256)/4];
int *bufa = (int*)((((int)buf)+255)&(-256));
void mk_buf(int size, int mode) {
int i;
switch (mode) {
case 0:
for (i=0; i<size/4; ++i) {
bufa[i] = -1;
}
break;
case 1:
for (i=0; i<size/4; ++i) {
*(float*)&bufa[i] = -1.0f;
}
break;
case 2:
for (i=0; i<size/4; i+=2) {
*(double*)&bufa[i] = -1.0;
}
break;
}
}
LONGLONG read_int(int count,int range) {
// LONGLONG c;
__asm {
mov esi,bufa
mov ebx,range
sub ebx,1
mov ecx,count
push ebp
sub esp,8
rdtsc
mov dword ptr [esp],eax
mov dword ptr [esp+4],edx
xor edx,edx
mov edi,-4*8
loop1:
mov eax,1024*1024/(4*8*2)
align 16
loop2:
mov ebp,[esi+edx+4*0]
mov ebp,[esi+edx+4*1]
add edi,4*8*2
and edi,ebx
mov ebp,[esi+edx+4*2]
mov ebp,[esi+edx+4*3]
mov ebp,[esi+edx+4*4]
mov ebp,[esi+edx+4*5]
mov ebp,[esi+edx+4*6]
mov ebp,[esi+edx+4*7]
mov ebp,[esi+edi+4*0]
mov ebp,[esi+edi+4*1]
add edx,4*8*2
and edx,ebx
mov ebp,[esi+edi+4*2]
mov ebp,[esi+edi+4*3]
mov ebp,[esi+edi+4*4]
mov ebp,[esi+edi+4*5]
mov ebp,[esi+edi+4*6]
mov ebp,[esi+edi+4*7]
sub eax,1
jne loop2
sub ecx,1
jne loop1
rdtsc
sub eax,dword ptr [esp]
sbb edx,dword ptr [esp+4]
add esp,8
pop ebp
}
}
LONGLONG read_x87(int count,int range) {
LONGLONG c;
__asm {
mov esi,bufa
mov ebx,range
sub ebx,1
rdtsc
mov dword ptr c,eax
mov dword ptr c+4,edx
xor edx,edx
mov edi,-8*8
mov ecx,count
loop1:
mov eax,1024*1024/(8*8*2)
align 16
loop2:
fld qword ptr [esi+edx+8*0]
fstp st(0)
fld qword ptr [esi+edx+8*1]
fstp st(0)
add edi,8*8*2
and edi,ebx
fld qword ptr [esi+edx+8*2]
fstp st(0)
fld qword ptr [esi+edx+8*3]
fstp st(0)
fld qword ptr [esi+edx+8*4]
fstp st(0)
fld qword ptr [esi+edx+8*5]
fstp st(0)
fld qword ptr [esi+edx+8*6]
fstp st(0)
fld qword ptr [esi+edx+8*7]
fstp st(0)
fld qword ptr [esi+edi+8*0]
fstp st(0)
fld qword ptr [esi+edi+8*1]
fstp st(0)
add edx,8*8*2
and edx,ebx
fld qword ptr [esi+edi+8*2]
fstp st(0)
fld qword ptr [esi+edi+8*3]
fstp st(0)
fld qword ptr [esi+edi+8*4]
fstp st(0)
fld qword ptr [esi+edi+8*5]
fstp st(0)
fld qword ptr [esi+edi+8*6]
fstp st(0)
fld qword ptr [esi+edi+8*7]
fstp st(0)
sub eax,1
jne loop2
sub ecx,1
jne loop1
rdtsc
sub eax,dword ptr c
sbb edx,dword ptr c+4
}
}
LONGLONG read_mmx(int count,int range) {
LONGLONG c;
__asm {
mov esi,bufa
mov ebx,range
sub ebx,1
rdtsc
mov dword ptr c,eax
mov dword ptr c+4,edx
xor edx,edx
mov edi,-8*8
mov ecx,count
loop1:
mov eax,1024*1024/(8*8*2)
align 16
loop2:
movq mm0,[esi+edx+8*0]
movq mm0,[esi+edx+8*1]
add edi,8*8*2
and edi,ebx
movq mm0,[esi+edx+8*2]
movq mm0,[esi+edx+8*3]
movq mm0,[esi+edx+8*4]
movq mm0,[esi+edx+8*5]
movq mm0,[esi+edx+8*6]
movq mm0,[esi+edx+8*7]
movq mm0,[esi+edi+8*0]
movq mm0,[esi+edi+8*1]
add edx,8*8*2
and edx,ebx
movq mm0,[esi+edi+8*2]
movq mm0,[esi+edi+8*3]
movq mm0,[esi+edi+8*4]
movq mm0,[esi+edi+8*5]
movq mm0,[esi+edi+8*6]
movq mm0,[esi+edi+8*7]
sub eax,1
jne loop2
sub ecx,1
jne loop1
rdtsc
sub eax,dword ptr c
sbb edx,dword ptr c+4
emms
}
}
LONGLONG read_sse(int count,int range) {
LONGLONG c;
__asm {
mov esi,bufa
mov ebx,range
sub ebx,1
rdtsc
mov dword ptr c,eax
mov dword ptr c+4,edx
xor edx,edx
mov edi,-16*8
mov ecx,count
loop1:
mov eax,1024*1024/(16*8*2)
align 16
loop2:
movaps xmm0,[esi+edx+16*0]
movaps xmm0,[esi+edx+16*1]
add edi,16*8*2
and edi,ebx
movaps xmm0,[esi+edx+16*2]
movaps xmm0,[esi+edx+16*3]
movaps xmm0,[esi+edx+16*4]
movaps xmm0,[esi+edx+16*5]
movaps xmm0,[esi+edx+16*6]
movaps xmm0,[esi+edx+16*7]
movaps xmm0,[esi+edi+16*0]
movaps xmm0,[esi+edi+16*1]
add edx,16*8*2
and edx,ebx
movaps xmm0,[esi+edi+16*2]
movaps xmm0,[esi+edi+16*3]
movaps xmm0,[esi+edi+16*4]
movaps xmm0,[esi+edi+16*5]
movaps xmm0,[esi+edi+16*6]
movaps xmm0,[esi+edi+16*7]
sub eax,1
jne loop2
sub ecx,1
jne loop1
rdtsc
sub eax,dword ptr c
sbb edx,dword ptr c+4
}
}
LONGLONG read_avx(int count,int range) {
LONGLONG c;
__asm {
mov esi,bufa
mov ebx,range
sub ebx,1
rdtsc
mov dword ptr c,eax
mov dword ptr c+4,edx
xor edx,edx
mov edi,-32*8
mov ecx,count
loop1:
mov eax,1024*1024/(32*8*2)
align 16
loop2:
vmovaps ymm0,[esi+edx+32*0]
vmovaps ymm0,[esi+edx+32*1]
add edi,32*8*2
and edi,ebx
vmovaps ymm0,[esi+edx+32*2]
vmovaps ymm0,[esi+edx+32*3]
vmovaps ymm0,[esi+edx+32*4]
vmovaps ymm0,[esi+edx+32*5]
vmovaps ymm0,[esi+edx+32*6]
vmovaps ymm0,[esi+edx+32*7]
vmovaps ymm0,[esi+edi+32*0]
vmovaps ymm0,[esi+edi+32*1]
add edx,32*8*2
and edx,ebx
vmovaps ymm0,[esi+edi+32*2]
vmovaps ymm0,[esi+edi+32*3]
vmovaps ymm0,[esi+edi+32*4]
vmovaps ymm0,[esi+edi+32*5]
vmovaps ymm0,[esi+edi+32*6]
vmovaps ymm0,[esi+edi+32*7]
sub eax,1
jne loop2
sub ecx,1
jne loop1
rdtsc
sub eax,dword ptr c
sbb edx,dword ptr c+4
}
}
void main() {
printf("load 帯域 計測ツール v0.4+(AVXサポートCPU専用)\n");
Sleep(1000);
CpuFreq cpufreq;
if (! cpufreq.Setup()) {
exit(0);
}
printf("CPU動作クロック : %.1f MHz\n", cpufreq.GetFreq() / 1000000.0);
HANDLE process = GetCurrentProcess();
LONGLONG (*test_func[])(int,int) = {read_int, read_x87, read_mmx, read_sse, read_avx};
char *test_name[] = {" Int32bit"," Float64bit", " MMX64bit", " SSE128bit", " AVX256bit"};
int test_mode[] = {0,2,0,1};
printf("\n");
printf("アクセス範囲 ");
int nf = 2;
int cpuid_edx;
int f;
for (f=0; f<5; ++f) {
printf(" %s", test_name[f]);
}
printf("\n");
int size;
for (size=1024; size<=BUFSIZE; size+=size) {
printf(" %5dKB :", size/1024);
int f;
for (f=0; f<5; ++f) {
mk_buf(size, test_mode[f]);
double minclk = 100000000000.0;
int i;
for (i=0; i<5; ++i) {
double clk;
int count = 10;
for (;;) {
Sleep(10);
SetPriorityClass(process, REALTIME_PRIORITY_CLASS);
test_func[f](1, size);
clk = test_func[f](count, size);
SetPriorityClass(process, NORMAL_PRIORITY_CLASS);
if (clk/cpufreq.GetFreq() >= 0.1) break;
count *= 2;
}
clk /= count;
if (clk < minclk) minclk = clk;
}
printf(" %6.0f MB/S", cpufreq.GetFreq()/minclk);
}
printf("\n");
}
fflush(stdin);
fprintf(stderr, "\n終了します。Enterキーを押してください : ");
scanf("%*c");
}
I2luY2x1ZGUgPHdpbmRvd3MuaD4KI2luY2x1ZGUgPHN0ZGlvLmg+CgpjbGFzcyBTdG9wV2F0Y2ggewoJTE9OR0xPTkcgc3RhcnRfdGltZSxsYXBfdGltZSxmcmVxOwpwdWJsaWM6CglTdG9wV2F0Y2goKSB7CgkJZnJlcSA9IDA7Cgl9Cgl+U3RvcFdhdGNoKCkgewoJfQoJYm9vbCBTZXR1cCgpIHsKCQlpZiAoISBRdWVyeVBlcmZvcm1hbmNlRnJlcXVlbmN5KChMQVJHRV9JTlRFR0VSKikmZnJlcSkpIHsKCQkJZnByaW50ZihzdGRlcnIsICLjgZPjga7jg4/jg7zjg4njgqbjgqfjgqLjga/jg5Hjg5Xjgqnjg7zjg57jg7Pjgrnjgqvjgqbjg7Pjgr/jgpLjgrXjg53jg7zjg4jjgZfjgabjgYTjgarjgYTngrrjgIHoqIjmuKzjgafjgY3jgb7jgZvjgpPjgIJcbiIpOwoJCQlyZXR1cm4gZmFsc2U7CgkJfQoJCXJldHVybiB0cnVlOwoJfQoJdm9pZCBTdGFydCgpIHsKCQlRdWVyeVBlcmZvcm1hbmNlQ291bnRlcigoTEFSR0VfSU5URUdFUiopJnN0YXJ0X3RpbWUpOwoJfQoJZG91YmxlIExhcFRpbWUoKSB7CgkJUXVlcnlQZXJmb3JtYW5jZUNvdW50ZXIoKExBUkdFX0lOVEVHRVIqKSZsYXBfdGltZSk7CgkJcmV0dXJuIChkb3VibGUpKGxhcF90aW1lLXN0YXJ0X3RpbWUpLyhkb3VibGUpZnJlcTsKCX0KfTsKCmNsYXNzIENwdUZyZXEgewoJU3RvcFdhdGNoIHN0b3B3YXRjaDsKCWRvdWJsZSBmcmVxOwpwdWJsaWM6CglDcHVGcmVxKCkgewoJfQoJfkNwdUZyZXEoKSB7Cgl9Cglib29sIFNldHVwKCkgewoJCWlmICghc3RvcHdhdGNoLlNldHVwKCkpIHsKCQkJcmV0dXJuIGZhbHNlOwoJCX0KCQlib29sIGV4Y2VwdF9mbGFnID0gZmFsc2U7CgkJX190cnkgewoJCQlfX2FzbSB7CgkJCQlyZHRzYwoJCQl9CgkJfSBfX2V4Y2VwdCAodHJ1ZSkgewoJCQlleGNlcHRfZmxhZyA9IHRydWU7CgkJfQoJCWlmIChleGNlcHRfZmxhZykgewoJCQlmcHJpbnRmKHN0ZGVyciwgIuOBk+OBrkNQVeOBr3JkdHNj5ZG95Luk44KS44K144Od44O844OI44GX44Gm44GE44Gq44GE54K644CB6KiI5ris44Gn44GN44G+44Gb44KT44CCXG4iKTsKCQkJcmV0dXJuIGZhbHNlOwoJCX0KCQkKCQlIQU5ETEUgcHJvY2VzcyA9IEdldEN1cnJlbnRQcm9jZXNzKCk7CgkJaW50IGk7CgkJZG91YmxlIG1heGY9MC4wOwoJCWZvciAoaT0wOyBpPDU7ICsraSkgewoJCQlTbGVlcCgxMCk7CgkJCVNldFByaW9yaXR5Q2xhc3MocHJvY2VzcywgUkVBTFRJTUVfUFJJT1JJVFlfQ0xBU1MpOwoJCQoJCQlMT05HTE9ORyBzdGFydGNvdW50LCBsYXBjb3VudDsKCQkJZG91YmxlIGxhcHRpbWU7CgkJCgkJCXN0b3B3YXRjaC5TdGFydCgpOwoKCQkJX19hc20gewoJCQkJcmR0c2MKCQkJCW1vdiBkd29yZCBwdHIgc3RhcnRjb3VudCwgZWF4CgkJCQltb3YgZHdvcmQgcHRyIHN0YXJ0Y291bnQrNCwgZWR4CgkJCX0KCgkJCXdoaWxlICgobGFwdGltZT1zdG9wd2F0Y2guTGFwVGltZSgpKSA8IDAuMSk7CgoJCQlfX2FzbSB7CgkJCQlyZHRzYwoJCQkJbW92IGR3b3JkIHB0ciBsYXBjb3VudCwgZWF4CgkJCQltb3YgZHdvcmQgcHRyIGxhcGNvdW50KzQsIGVkeAoJCQl9CgkJCgkJCVNldFByaW9yaXR5Q2xhc3MocHJvY2VzcywgTk9STUFMX1BSSU9SSVRZX0NMQVNTKTsKCgkJCWRvdWJsZSBmID0gKGRvdWJsZSkobGFwY291bnQtc3RhcnRjb3VudCkgLyBsYXB0aW1lOwoJCQlpZiAoZiA+IG1heGYpIG1heGYgPSBmOwoJCX0KCQlmcmVxID0gbWF4ZjsKCQkKCQlyZXR1cm4gdHJ1ZTsKCX0KCWRvdWJsZSBHZXRGcmVxKCkgewoJCXJldHVybiBmcmVxOwoJfQp9OwoKCiNkZWZpbmUgTU1YX0ZMQUcJMHgwMDgwMDAwMAojZGVmaW5lIFNTRV9GTEFHCTB4MDIwMDAwMDAKI2RlZmluZSBTU0UyX0ZMQUcJMHgwNDAwMDAwMAoKCiNkZWZpbmUgQlVGU0laRSAoMzIqMTAyNCoxMDI0KQoKaW50IGJ1ZlsoQlVGU0laRSsyNTYpLzRdOwppbnQgKmJ1ZmEgPSAoaW50KikoKCgoaW50KWJ1ZikrMjU1KSYoLTI1NikpOwoKdm9pZCBta19idWYoaW50IHNpemUsIGludCBtb2RlKSB7CglpbnQgaTsKCXN3aXRjaCAobW9kZSkgewoJY2FzZSAwOgoJCWZvciAoaT0wOyBpPHNpemUvNDsgKytpKSB7CgkJCWJ1ZmFbaV0gPSAtMTsKCQl9CgkJYnJlYWs7CgljYXNlIDE6CgkJZm9yIChpPTA7IGk8c2l6ZS80OyArK2kpIHsKCQkJKihmbG9hdCopJmJ1ZmFbaV0gPSAtMS4wZjsKCQl9CgkJYnJlYWs7CgljYXNlIDI6CgkJZm9yIChpPTA7IGk8c2l6ZS80OyBpKz0yKSB7CgkJCSooZG91YmxlKikmYnVmYVtpXSA9IC0xLjA7CgkJfQoJCWJyZWFrOwoJfQoKfQoKTE9OR0xPTkcgcmVhZF9pbnQoaW50IGNvdW50LGludCByYW5nZSkgewovLwlMT05HTE9ORyBjOwoJX19hc20gewoJCW1vdiBlc2ksYnVmYQoKCQltb3YgZWJ4LHJhbmdlCgkJc3ViIGVieCwxCgoJCW1vdiBlY3gsY291bnQKCgkJcHVzaCBlYnAKCQlzdWIgZXNwLDgKCgkJcmR0c2MKCQltb3YgZHdvcmQgcHRyIFtlc3BdLGVheAoJCW1vdiBkd29yZCBwdHIgW2VzcCs0XSxlZHgKCgkJeG9yIGVkeCxlZHgKCQltb3YgZWRpLC00KjgKCQkKCQlsb29wMToKCQkJbW92IGVheCwxMDI0KjEwMjQvKDQqOCoyKQoJCQlhbGlnbiAxNgoJCQlsb29wMjoKCQkJCW1vdiBlYnAsW2VzaStlZHgrNCowXQoJCQkJbW92IGVicCxbZXNpK2VkeCs0KjFdCgkJCQlhZGQgZWRpLDQqOCoyCgkJCQlhbmQgZWRpLGVieAoJCQkJbW92IGVicCxbZXNpK2VkeCs0KjJdCgkJCQltb3YgZWJwLFtlc2krZWR4KzQqM10KCQkJCW1vdiBlYnAsW2VzaStlZHgrNCo0XQoJCQkJbW92IGVicCxbZXNpK2VkeCs0KjVdCgkJCQltb3YgZWJwLFtlc2krZWR4KzQqNl0KCQkJCW1vdiBlYnAsW2VzaStlZHgrNCo3XQoKCQkJCW1vdiBlYnAsW2VzaStlZGkrNCowXQoJCQkJbW92IGVicCxbZXNpK2VkaSs0KjFdCgkJCQlhZGQgZWR4LDQqOCoyCgkJCQlhbmQgZWR4LGVieAoJCQkJbW92IGVicCxbZXNpK2VkaSs0KjJdCgkJCQltb3YgZWJwLFtlc2krZWRpKzQqM10KCQkJCW1vdiBlYnAsW2VzaStlZGkrNCo0XQoJCQkJbW92IGVicCxbZXNpK2VkaSs0KjVdCgkJCQltb3YgZWJwLFtlc2krZWRpKzQqNl0KCQkJCW1vdiBlYnAsW2VzaStlZGkrNCo3XQoJCgkJCXN1YiBlYXgsMQoJCQlqbmUgbG9vcDIKCQkKCQlzdWIgZWN4LDEKCQlqbmUgbG9vcDEKCgkJcmR0c2MKCQlzdWIgZWF4LGR3b3JkIHB0ciBbZXNwXQoJCXNiYiBlZHgsZHdvcmQgcHRyIFtlc3ArNF0KCgkJYWRkIGVzcCw4CgkJcG9wIGVicAoJfQp9CgpMT05HTE9ORyByZWFkX3g4NyhpbnQgY291bnQsaW50IHJhbmdlKSB7CglMT05HTE9ORyBjOwoJX19hc20gewoJCW1vdiBlc2ksYnVmYQoJCW1vdiBlYngscmFuZ2UKCQlzdWIgZWJ4LDEKCgkJcmR0c2MKCQltb3YgZHdvcmQgcHRyIGMsZWF4CgkJbW92IGR3b3JkIHB0ciBjKzQsZWR4CgoJCXhvciBlZHgsZWR4CgkJbW92IGVkaSwtOCo4CgkJCgkJbW92IGVjeCxjb3VudAoJCWxvb3AxOgoJCQltb3YgZWF4LDEwMjQqMTAyNC8oOCo4KjIpCgkJCWFsaWduIDE2CgkJCWxvb3AyOgoJCQkJZmxkIHF3b3JkIHB0ciBbZXNpK2VkeCs4KjBdCgkJCQlmc3RwIHN0KDApCgkJCQlmbGQgcXdvcmQgcHRyIFtlc2krZWR4KzgqMV0KCQkJCWZzdHAgc3QoMCkKCQkJCWFkZCBlZGksOCo4KjIKCQkJCWFuZCBlZGksZWJ4CgkJCQlmbGQgcXdvcmQgcHRyIFtlc2krZWR4KzgqMl0KCQkJCWZzdHAgc3QoMCkKCQkJCWZsZCBxd29yZCBwdHIgW2VzaStlZHgrOCozXQoJCQkJZnN0cCBzdCgwKQoJCQkJZmxkIHF3b3JkIHB0ciBbZXNpK2VkeCs4KjRdCgkJCQlmc3RwIHN0KDApCgkJCQlmbGQgcXdvcmQgcHRyIFtlc2krZWR4KzgqNV0KCQkJCWZzdHAgc3QoMCkKCQkJCWZsZCBxd29yZCBwdHIgW2VzaStlZHgrOCo2XQoJCQkJZnN0cCBzdCgwKQoJCQkJZmxkIHF3b3JkIHB0ciBbZXNpK2VkeCs4KjddCgkJCQlmc3RwIHN0KDApCgoJCQkJZmxkIHF3b3JkIHB0ciBbZXNpK2VkaSs4KjBdCgkJCQlmc3RwIHN0KDApCgkJCQlmbGQgcXdvcmQgcHRyIFtlc2krZWRpKzgqMV0KCQkJCWZzdHAgc3QoMCkKCQkJCWFkZCBlZHgsOCo4KjIKCQkJCWFuZCBlZHgsZWJ4CgkJCQlmbGQgcXdvcmQgcHRyIFtlc2krZWRpKzgqMl0KCQkJCWZzdHAgc3QoMCkKCQkJCWZsZCBxd29yZCBwdHIgW2VzaStlZGkrOCozXQoJCQkJZnN0cCBzdCgwKQoJCQkJZmxkIHF3b3JkIHB0ciBbZXNpK2VkaSs4KjRdCgkJCQlmc3RwIHN0KDApCgkJCQlmbGQgcXdvcmQgcHRyIFtlc2krZWRpKzgqNV0KCQkJCWZzdHAgc3QoMCkKCQkJCWZsZCBxd29yZCBwdHIgW2VzaStlZGkrOCo2XQoJCQkJZnN0cCBzdCgwKQoJCQkJZmxkIHF3b3JkIHB0ciBbZXNpK2VkaSs4KjddCgkJCQlmc3RwIHN0KDApCgkJCQoJCQlzdWIgZWF4LDEKCQkJam5lIGxvb3AyCgkJCgkJc3ViIGVjeCwxCgkJam5lIGxvb3AxCgoJCXJkdHNjCgkJc3ViIGVheCxkd29yZCBwdHIgYwoJCXNiYiBlZHgsZHdvcmQgcHRyIGMrNAoKCX0KfQoKTE9OR0xPTkcgcmVhZF9tbXgoaW50IGNvdW50LGludCByYW5nZSkgewoJTE9OR0xPTkcgYzsKCV9fYXNtIHsKCQltb3YgZXNpLGJ1ZmEKCQltb3YgZWJ4LHJhbmdlCgkJc3ViIGVieCwxCgoJCXJkdHNjCgkJbW92IGR3b3JkIHB0ciBjLGVheAoJCW1vdiBkd29yZCBwdHIgYys0LGVkeAoKCQl4b3IgZWR4LGVkeAoJCW1vdiBlZGksLTgqOAoJCQoJCW1vdiBlY3gsY291bnQKCQlsb29wMToKCQkJbW92IGVheCwxMDI0KjEwMjQvKDgqOCoyKQoJCQlhbGlnbiAxNgoJCQlsb29wMjoKCQkJCW1vdnEgbW0wLFtlc2krZWR4KzgqMF0KCQkJCW1vdnEgbW0wLFtlc2krZWR4KzgqMV0KCQkJCWFkZCBlZGksOCo4KjIKCQkJCWFuZCBlZGksZWJ4CgkJCQltb3ZxIG1tMCxbZXNpK2VkeCs4KjJdCgkJCQltb3ZxIG1tMCxbZXNpK2VkeCs4KjNdCgkJCQltb3ZxIG1tMCxbZXNpK2VkeCs4KjRdCgkJCQltb3ZxIG1tMCxbZXNpK2VkeCs4KjVdCgkJCQltb3ZxIG1tMCxbZXNpK2VkeCs4KjZdCgkJCQltb3ZxIG1tMCxbZXNpK2VkeCs4KjddCgoJCQkJbW92cSBtbTAsW2VzaStlZGkrOCowXQoJCQkJbW92cSBtbTAsW2VzaStlZGkrOCoxXQoJCQkJYWRkIGVkeCw4KjgqMgoJCQkJYW5kIGVkeCxlYngKCQkJCW1vdnEgbW0wLFtlc2krZWRpKzgqMl0KCQkJCW1vdnEgbW0wLFtlc2krZWRpKzgqM10KCQkJCW1vdnEgbW0wLFtlc2krZWRpKzgqNF0KCQkJCW1vdnEgbW0wLFtlc2krZWRpKzgqNV0KCQkJCW1vdnEgbW0wLFtlc2krZWRpKzgqNl0KCQkJCW1vdnEgbW0wLFtlc2krZWRpKzgqN10KCQkJCgkJCXN1YiBlYXgsMQoJCQlqbmUgbG9vcDIKCQkKCQlzdWIgZWN4LDEKCQlqbmUgbG9vcDEKCgkJcmR0c2MKCQlzdWIgZWF4LGR3b3JkIHB0ciBjCgkJc2JiIGVkeCxkd29yZCBwdHIgYys0CgoJCWVtbXMKCgl9Cn0KCkxPTkdMT05HIHJlYWRfc3NlKGludCBjb3VudCxpbnQgcmFuZ2UpIHsKCUxPTkdMT05HIGM7CglfX2FzbSB7CgkJbW92IGVzaSxidWZhCgkJbW92IGVieCxyYW5nZQoJCXN1YiBlYngsMQoKCQlyZHRzYwoJCW1vdiBkd29yZCBwdHIgYyxlYXgKCQltb3YgZHdvcmQgcHRyIGMrNCxlZHgKCgkJeG9yIGVkeCxlZHgKCQltb3YgZWRpLC0xNio4CgkJCgkJbW92IGVjeCxjb3VudAoJCWxvb3AxOgoJCQltb3YgZWF4LDEwMjQqMTAyNC8oMTYqOCoyKQoJCQlhbGlnbiAxNgoJCQlsb29wMjoKCQkJCW1vdmFwcyB4bW0wLFtlc2krZWR4KzE2KjBdCgkJCQltb3ZhcHMgeG1tMCxbZXNpK2VkeCsxNioxXQoJCQkJYWRkIGVkaSwxNio4KjIKCQkJCWFuZCBlZGksZWJ4CgkJCQltb3ZhcHMgeG1tMCxbZXNpK2VkeCsxNioyXQoJCQkJbW92YXBzIHhtbTAsW2VzaStlZHgrMTYqM10KCQkJCW1vdmFwcyB4bW0wLFtlc2krZWR4KzE2KjRdCgkJCQltb3ZhcHMgeG1tMCxbZXNpK2VkeCsxNio1XQoJCQkJbW92YXBzIHhtbTAsW2VzaStlZHgrMTYqNl0KCQkJCW1vdmFwcyB4bW0wLFtlc2krZWR4KzE2KjddCgoJCQkJbW92YXBzIHhtbTAsW2VzaStlZGkrMTYqMF0KCQkJCW1vdmFwcyB4bW0wLFtlc2krZWRpKzE2KjFdCgkJCQlhZGQgZWR4LDE2KjgqMgoJCQkJYW5kIGVkeCxlYngKCQkJCW1vdmFwcyB4bW0wLFtlc2krZWRpKzE2KjJdCgkJCQltb3ZhcHMgeG1tMCxbZXNpK2VkaSsxNiozXQoJCQkJbW92YXBzIHhtbTAsW2VzaStlZGkrMTYqNF0KCQkJCW1vdmFwcyB4bW0wLFtlc2krZWRpKzE2KjVdCgkJCQltb3ZhcHMgeG1tMCxbZXNpK2VkaSsxNio2XQoJCQkJbW92YXBzIHhtbTAsW2VzaStlZGkrMTYqN10KCQkJCgkJCXN1YiBlYXgsMQoJCQlqbmUgbG9vcDIKCQkKCQlzdWIgZWN4LDEKCQlqbmUgbG9vcDEKCgkJcmR0c2MKCQlzdWIgZWF4LGR3b3JkIHB0ciBjCgkJc2JiIGVkeCxkd29yZCBwdHIgYys0CgoJfQp9CgoKTE9OR0xPTkcgcmVhZF9hdngoaW50IGNvdW50LGludCByYW5nZSkgewoJTE9OR0xPTkcgYzsKCV9fYXNtIHsKCQltb3YgZXNpLGJ1ZmEKCQltb3YgZWJ4LHJhbmdlCgkJc3ViIGVieCwxCgoJCXJkdHNjCgkJbW92IGR3b3JkIHB0ciBjLGVheAoJCW1vdiBkd29yZCBwdHIgYys0LGVkeAoKCQl4b3IgZWR4LGVkeAoJCW1vdiBlZGksLTMyKjgKCQkKCQltb3YgZWN4LGNvdW50CgkJbG9vcDE6CgkJCW1vdiBlYXgsMTAyNCoxMDI0LygzMio4KjIpCgkJCWFsaWduIDE2CgkJCWxvb3AyOgoJCQkJdm1vdmFwcyB5bW0wLFtlc2krZWR4KzMyKjBdCgkJCQl2bW92YXBzIHltbTAsW2VzaStlZHgrMzIqMV0KCQkJCWFkZCBlZGksMzIqOCoyCgkJCQlhbmQgZWRpLGVieAoJCQkJdm1vdmFwcyB5bW0wLFtlc2krZWR4KzMyKjJdCgkJCQl2bW92YXBzIHltbTAsW2VzaStlZHgrMzIqM10KCQkJCXZtb3ZhcHMgeW1tMCxbZXNpK2VkeCszMio0XQoJCQkJdm1vdmFwcyB5bW0wLFtlc2krZWR4KzMyKjVdCgkJCQl2bW92YXBzIHltbTAsW2VzaStlZHgrMzIqNl0KCQkJCXZtb3ZhcHMgeW1tMCxbZXNpK2VkeCszMio3XQoKCQkJCXZtb3ZhcHMgeW1tMCxbZXNpK2VkaSszMiowXQoJCQkJdm1vdmFwcyB5bW0wLFtlc2krZWRpKzMyKjFdCgkJCQlhZGQgZWR4LDMyKjgqMgoJCQkJYW5kIGVkeCxlYngKCQkJCXZtb3ZhcHMgeW1tMCxbZXNpK2VkaSszMioyXQoJCQkJdm1vdmFwcyB5bW0wLFtlc2krZWRpKzMyKjNdCgkJCQl2bW92YXBzIHltbTAsW2VzaStlZGkrMzIqNF0KCQkJCXZtb3ZhcHMgeW1tMCxbZXNpK2VkaSszMio1XQoJCQkJdm1vdmFwcyB5bW0wLFtlc2krZWRpKzMyKjZdCgkJCQl2bW92YXBzIHltbTAsW2VzaStlZGkrMzIqN10KCQkJCgkJCXN1YiBlYXgsMQoJCQlqbmUgbG9vcDIKCQkKCQlzdWIgZWN4LDEKCQlqbmUgbG9vcDEKCgkJcmR0c2MKCQlzdWIgZWF4LGR3b3JkIHB0ciBjCgkJc2JiIGVkeCxkd29yZCBwdHIgYys0CgoJfQp9Cgp2b2lkIG1haW4oKSB7CglwcmludGYoImxvYWQg5biv5Z+fIOioiOa4rOODhOODvOODqyB2MC40KyhBVljjgrXjg53jg7zjg4hDUFXlsILnlKgpXG4iKTsKCglTbGVlcCgxMDAwKTsKCglDcHVGcmVxIGNwdWZyZXE7CglpZiAoISBjcHVmcmVxLlNldHVwKCkpIHsKCQlleGl0KDApOwoJfQoJcHJpbnRmKCJDUFXli5XkvZzjgq/jg63jg4Pjgq8gOiAlLjFmIE1IelxuIiwgY3B1ZnJlcS5HZXRGcmVxKCkgLyAxMDAwMDAwLjApOwoKCUhBTkRMRSBwcm9jZXNzID0gR2V0Q3VycmVudFByb2Nlc3MoKTsKCglMT05HTE9ORyAoKnRlc3RfZnVuY1tdKShpbnQsaW50KSA9IHtyZWFkX2ludCwgcmVhZF94ODcsIHJlYWRfbW14LCByZWFkX3NzZSwgcmVhZF9hdnh9OwoJY2hhciAqdGVzdF9uYW1lW10gPSB7IiAgSW50MzJiaXQiLCIgRmxvYXQ2NGJpdCIsICIgICBNTVg2NGJpdCIsICIgIFNTRTEyOGJpdCIsICIgIEFWWDI1NmJpdCJ9OwoJaW50IHRlc3RfbW9kZVtdID0gezAsMiwwLDF9OwoJCQoJcHJpbnRmKCJcbiIpOwoJcHJpbnRmKCLvvbHvvbjvvb7vvb3nr4Tlm7IgICAiKTsKCWludCBuZiA9IDI7CglpbnQgY3B1aWRfZWR4OwoKCWludCBmOwoJZm9yIChmPTA7IGY8NTsgKytmKSB7CgkJcHJpbnRmKCIgJXMiLCB0ZXN0X25hbWVbZl0pOwoJfQoJCglwcmludGYoIlxuIik7CglpbnQgc2l6ZTsKCWZvciAoc2l6ZT0xMDI0OyBzaXplPD1CVUZTSVpFOyBzaXplKz1zaXplKSB7CgkKCQlwcmludGYoIiAlNWRLQiA6Iiwgc2l6ZS8xMDI0KTsKCgkJaW50IGY7CgkJZm9yIChmPTA7IGY8NTsgKytmKSB7CgoJCQlta19idWYoc2l6ZSwgdGVzdF9tb2RlW2ZdKTsKCQoJCQlkb3VibGUgbWluY2xrID0gMTAwMDAwMDAwMDAwLjA7CgkJCWludCBpOwoJCQlmb3IgKGk9MDsgaTw1OyArK2kpIHsKCQkJCWRvdWJsZSBjbGs7CgkJCQlpbnQgY291bnQgPSAxMDsKCQkJCWZvciAoOzspIHsKCQkJCQlTbGVlcCgxMCk7CgkJCQkJU2V0UHJpb3JpdHlDbGFzcyhwcm9jZXNzLCBSRUFMVElNRV9QUklPUklUWV9DTEFTUyk7CgkJCQoJCQkJCXRlc3RfZnVuY1tmXSgxLCBzaXplKTsKCQkJCQljbGsgPSB0ZXN0X2Z1bmNbZl0oY291bnQsIHNpemUpOwoKCQkJCQlTZXRQcmlvcml0eUNsYXNzKHByb2Nlc3MsIE5PUk1BTF9QUklPUklUWV9DTEFTUyk7CgoJCQkJCWlmIChjbGsvY3B1ZnJlcS5HZXRGcmVxKCkgPj0gMC4xKSBicmVhazsKCgkJCQkJY291bnQgKj0gMjsKCQkJCX0KCQkJCWNsayAvPSBjb3VudDsKCQkJCWlmIChjbGsgPCBtaW5jbGspIG1pbmNsayA9IGNsazsKCQkJfQoKCQkJcHJpbnRmKCIgJTYuMGYgTUIvUyIsIGNwdWZyZXEuR2V0RnJlcSgpL21pbmNsayk7CgkJfQoKCQlwcmludGYoIlxuIik7Cgl9CgoJZmZsdXNoKHN0ZGluKTsKICAgCWZwcmludGYoc3RkZXJyLCAiXG7ntYLkuobjgZfjgb7jgZnjgIJFbnRlcuOCreODvOOCkuaKvOOBl+OBpuOBj+OBoOOBleOBhCA6ICIpOwoJc2NhbmYoIiUqYyIpOwp9Cg==