Part 1
input<-read_lines("../../AoCData/AOC2019/Day22.txt")
For part 1, I’m manipulating the deck
This reverses the deck:
newstack<-function(d){
rev(d)}
This cuts it (both pos & neg work)
cutdeck<-function(d,n){
n<-n%%length(d)
outdeck<-d
outdeck<-c(d[-c(1:n)],d[c(1:n)])
outdeck}
This does the “deal with increment” shuffle
dwi<-function(d,n){
outdeck<-c()
for(i in 1:length(d)){
x<-((i-1)*n+1)%%length(d)
if(x==0){x<-length(d)}
outdeck[x]<-d[i]}
outdeck}
This function reads in the instructions, does them all and returns a
shuffled, manipulated deck
shufflepart1<-function(d,instructions){
i<-1
while(i<=length(instructions)){
inst<-unlist(str_split(instructions[i]," "))
### if the first word is cut, cut
if(inst[1]=="cut"){
d<-cutdeck(d,as.numeric(inst[2]))
### otherwise if the second word is "into" reverse the deck
}else if(inst[2]=="into"){
d<-newstack(d)
### otherwise if the second word is "with" then deal with increment
}else if(inst[2]=="with"){
d<-dwi(d,as.numeric(inst[4]))
}else{cat("Someting went wrong\n")}
i<-i+1}
d}
Part 1 - run this against my input & get the answer
p1<-shufflepart1(0:10006,input)
part1<-which(p1==2019)-1
part1
[1] 2604
p1<-0:10006
x<-2019
for(i in 1:10){
p1<-shufflepart1(p1,input)
x<-which(p1==2019)-1
cat(x,"\n")
}
2604
1911
9198
6262
6507
5447
9012
940
7885
5816
Part 2 -
My attempt to use modular arithmetic:
This takes the decksize & the instructions and returns with the
coefficients of the equation that would solve it in one go:
modtransform<-function(decksize,instructions){
a<-1
b<-0
m<-decksize
i<-1
while(i<=length(instructions)){
inst<-unlist(str_split(instructions[i]," "))
### cut
if(inst[1]=="cut"){
c<-1
d<- -as.numeric(inst[2])
### reverse
}else if(inst[2]=="into"){
c<- -1
d<- -1
### deal with increment
}else if(inst[2]=="with"){
c<- as.numeric(inst[4])
d<- 0
}else{cat("Someting went wrong\n")}
a<-(a*c)%%m
b<-(b*c+d)%%m
i<-i+1}
c(a,b)}
For a deck size of 10007, this is the answer:
modtransform(10007,input)
[1] 9390 7459
###
The A is 9390. The B is 7459.
For a sanity check - this is what the old method gives for the
position of card 2019 the first 10 shuffles of a 10007 card deck
p1<-0:10006
x<-2019
oldmethod<-c()
for(i in 1:10){
p1<-shufflepart1(p1,input)
x<-which(p1==2019)-1
oldmethod<-c(oldmethod,x)
}
oldmethod
[1] 2604 1911 9198 6262 6507 5447 9012 940 7885 5816
And this is what the new method gives
crd<-2019
newmethod<-c()
for(i in 1:10){
crd<-(9390*crd+7459)%%10007
newmethod<-c(newmethod,crd)}
newmethod
[1] 2604 1911 9198 6262 6507 5447 9012 940 7885 5816
cat("compare old method and new method: ", oldmethod==newmethod,"\n")
compare old method and new method: TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
They are the same - so I think that the modular is working.
This is just the function for composing Ax+B onto Cx+D mod M
cmps<-function(a,b,c,d,m){
a<-(a*c)%%m
b<-((b*c)+d)%%m
c(a,b)}
This takes the function Ax+B and raises it to the k (shuffles
multiple times)
multishuffle<-function(a,b,k,decksize,t){
c<-1
d<-0
while(k>0){
if(k%%2==1){
g<-cmps(a,b,c,d,decksize)
c<-g[1]
d<-g[2]}
k<-k%/%2
f<-cmps(a,b,a,b,decksize)
a<-f[1]
b<-f[2]}
(c*t+d)%%decksize}
And again, using 10007 and card 2019 - this is where it goes for the
first 10 shuffles
multishuffleversion<-c()
for(i in 1:10){multishuffleversion<-c(multishuffleversion,multishuffle(9390,7459,i,10007,2019))}
multishuffleversion
[1] 2604 1911 9198 6262 6507 5447 9012 940 7885 5816
cat("compare new method & multishuffle: ", multishuffleversion==newmethod,"\n")
compare new method & multishuffle: TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
So this gives the same answer
BUT!!!!!!!!!!
The question asks for the card in position 2020, not where 2020 ends
up. So, it needs the inverse function
And to find that, you find the inverse of A for that deck size
### A
modinv(9390,10007)
[1] 519
### B
(modinv(9390,10007)*-7459)%%10007
[1] 1488
So, to find what card was in that position, you can use 519 &
8519 (with these instructions on a deck of 10007) To show that makes
sense, try it on the first 10 shuffle positions - all of them should
return 2019:
sapply(1:10,function(x){multishuffle(519,1488,x,10007,multishuffleversion[x])})
[1] 2019 2019 2019 2019 2019 2019 2019 2019 2019 2019
multishuffle(519,1488,10,10007,2020)
[1] 9248
Exactly as expected.
So trying this with a really, really, really big deck and a lot of
shuffles:
largedeck<-as.bigz("119315717514047")
largeshuffle<-as.bigz("101741582076661")
AB<-modtransform(largedeck,input)
A<-as.bigz(AB[1])
B<-as.bigz(AB[2])
A
Big Integer ('bigz') :
[1] 28480444974763
B
Big Integer ('bigz') :
[1] 104343029082553
A=28480444974763 B=104343029082553
Invert the A & B:
### A
#iA<-modinv(A,largedeck)
### B
#iB<-(modinv(A,largedeck)*-B)%%largeDeck
Using wolfram alpha - iA = 82675948719931 iB = 403183578768
iA<-as.bigz("82675948719931")
iB<-as.bigz("403183578768")
And run that through multishuffle:
multishuffle(iA,iB,largeshuffle,largedeck,2020)
Big Integer ('bigz') :
[1] 79608410258462
So, trying this backwards:
This function does the reverse of everything. It cuts backwards, it
reverses (which is the same either way), it deals into backwards.
inverstrans<-function(decksize,instructions){
a<-1
b<-0
m<-decksize
i<-1
while(i<=length(instructions)){
inst<-unlist(str_split(instructions[i]," "))
if(inst[1]=="cut"){
c<-1
d<- -as.numeric(inst[2])
}else if(inst[2]=="into"){
c<- -1
d<- -1
}else if(inst[2]=="with"){
c<- as.numeric(inst[4])
d<- 0
}else{cat("Someting went wrong\n")}
### invert it
invc<-modinv(c,m)%%m
invd<- (-d*modinv(c,m))%%m
###
a<-(a*invc)%%m
b<-(b*invc+invd)%%m
i<-i+1}
### realigns to be 0 indexed
c(a,b)}
Again showing that it works with 10007
inverstrans(10007,rev(input))
[1] 519 1488
And those numbers may look familiar - because they’re the same as
above. And so it’s clear that this should work
This doesn’t work on the really big deck - because I can’t run bigz
through modinv
LS0tDQp0aXRsZTogIkRheSAyMiBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShnbXApDQpsaWJyYXJ5KG51bWJlcnMpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGNvbGxlY3Rpb25zKQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpDQpgYGANCg0KDQojIyBQYXJ0IDENCg0KYGBge3J9DQppbnB1dDwtcmVhZF9saW5lcygiLi4vLi4vQW9DRGF0YS9BT0MyMDE5L0RheTIyLnR4dCIpDQpgYGANCg0KRm9yIHBhcnQgMSwgSSdtIG1hbmlwdWxhdGluZyB0aGUgZGVjaw0KDQpUaGlzIHJldmVyc2VzIHRoZSBkZWNrOg0KYGBge3J9DQpuZXdzdGFjazwtZnVuY3Rpb24oZCl7DQogIHJldihkKX0NCmBgYA0KDQpUaGlzIGN1dHMgaXQgKGJvdGggcG9zICYgbmVnIHdvcmspDQpgYGB7cn0NCmN1dGRlY2s8LWZ1bmN0aW9uKGQsbil7DQogIG48LW4lJWxlbmd0aChkKQ0KICBvdXRkZWNrPC1kDQogIG91dGRlY2s8LWMoZFstYygxOm4pXSxkW2MoMTpuKV0pDQogIG91dGRlY2t9DQpgYGANCg0KVGhpcyBkb2VzIHRoZSAiZGVhbCB3aXRoIGluY3JlbWVudCIgc2h1ZmZsZQ0KDQpgYGB7cn0NCmR3aTwtZnVuY3Rpb24oZCxuKXsNCiAgb3V0ZGVjazwtYygpDQogIGZvcihpIGluIDE6bGVuZ3RoKGQpKXsNCiAgICB4PC0oKGktMSkqbisxKSUlbGVuZ3RoKGQpDQogICAgaWYoeD09MCl7eDwtbGVuZ3RoKGQpfQ0KICAgIG91dGRlY2tbeF08LWRbaV19DQogIG91dGRlY2t9DQpgYGANCg0KVGhpcyBmdW5jdGlvbiByZWFkcyBpbiB0aGUgaW5zdHJ1Y3Rpb25zLCBkb2VzIHRoZW0gYWxsIGFuZCByZXR1cm5zIGEgc2h1ZmZsZWQsIG1hbmlwdWxhdGVkIGRlY2sNCg0KYGBge3J9DQpzaHVmZmxlcGFydDE8LWZ1bmN0aW9uKGQsaW5zdHJ1Y3Rpb25zKXsNCiAgaTwtMQ0KICB3aGlsZShpPD1sZW5ndGgoaW5zdHJ1Y3Rpb25zKSl7DQogICAgaW5zdDwtdW5saXN0KHN0cl9zcGxpdChpbnN0cnVjdGlvbnNbaV0sIiAiKSkNCiAgICAjIyMgaWYgdGhlIGZpcnN0IHdvcmQgaXMgY3V0LCBjdXQNCiAgICBpZihpbnN0WzFdPT0iY3V0Iil7DQogICAgICBkPC1jdXRkZWNrKGQsYXMubnVtZXJpYyhpbnN0WzJdKSkNCiAgICAjIyMgb3RoZXJ3aXNlIGlmIHRoZSBzZWNvbmQgd29yZCBpcyAiaW50byIgcmV2ZXJzZSB0aGUgZGVjaw0KICAgIH1lbHNlIGlmKGluc3RbMl09PSJpbnRvIil7DQogICAgICBkPC1uZXdzdGFjayhkKQ0KICAgICMjIyBvdGhlcndpc2UgaWYgdGhlIHNlY29uZCB3b3JkIGlzICJ3aXRoIiB0aGVuIGRlYWwgd2l0aCBpbmNyZW1lbnQNCiAgICB9ZWxzZSBpZihpbnN0WzJdPT0id2l0aCIpew0KICAgICAgZDwtZHdpKGQsYXMubnVtZXJpYyhpbnN0WzRdKSkNCiAgICB9ZWxzZXtjYXQoIlNvbWV0aW5nIHdlbnQgd3JvbmdcbiIpfQ0KICAgIGk8LWkrMX0NCiAgZH0NCmBgYA0KDQpQYXJ0IDEgLSBydW4gdGhpcyBhZ2FpbnN0IG15IGlucHV0ICYgZ2V0IHRoZSBhbnN3ZXINCg0KYGBge3J9DQpwMTwtc2h1ZmZsZXBhcnQxKDA6MTAwMDYsaW5wdXQpDQpwYXJ0MTwtd2hpY2gocDE9PTIwMTkpLTENCnBhcnQxDQoNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnAxPC0wOjEwMDA2DQp4PC0yMDE5DQpmb3IoaSBpbiAxOjEwKXsNCiAgcDE8LXNodWZmbGVwYXJ0MShwMSxpbnB1dCkNCiAgeDwtd2hpY2gocDE9PTIwMTkpLTENCiAgY2F0KHgsIlxuIikNCn0NCmBgYA0KDQoNCg0KIyMgUGFydCAyIC0gDQoNCk15IGF0dGVtcHQgdG8gdXNlIG1vZHVsYXIgYXJpdGhtZXRpYzoNCg0KDQpUaGlzIHRha2VzIHRoZSBkZWNrc2l6ZSAmIHRoZSBpbnN0cnVjdGlvbnMgYW5kIHJldHVybnMgd2l0aCB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBlcXVhdGlvbiB0aGF0IHdvdWxkIHNvbHZlIGl0IGluIG9uZSBnbzogDQpgYGB7cn0NCm1vZHRyYW5zZm9ybTwtZnVuY3Rpb24oZGVja3NpemUsaW5zdHJ1Y3Rpb25zKXsNCiAgYTwtMQ0KICBiPC0wDQogIG08LWRlY2tzaXplDQogIGk8LTENCiAgd2hpbGUoaTw9bGVuZ3RoKGluc3RydWN0aW9ucykpew0KICAgIGluc3Q8LXVubGlzdChzdHJfc3BsaXQoaW5zdHJ1Y3Rpb25zW2ldLCIgIikpDQogICAgDQogICAgIyMjIGN1dA0KICAgIGlmKGluc3RbMV09PSJjdXQiKXsNCiAgICAgIGM8LTENCiAgICAgIGQ8LSAtYXMubnVtZXJpYyhpbnN0WzJdKQ0KICAgICMjIyByZXZlcnNlDQogICAgfWVsc2UgaWYoaW5zdFsyXT09ImludG8iKXsNCiAgICAgIGM8LSAtMQ0KICAgICAgZDwtIC0xDQogICAgIyMjIGRlYWwgd2l0aCBpbmNyZW1lbnQNCiAgICB9ZWxzZSBpZihpbnN0WzJdPT0id2l0aCIpew0KICAgICAgYzwtIGFzLm51bWVyaWMoaW5zdFs0XSkNCiAgICAgIGQ8LSAwDQogICAgfWVsc2V7Y2F0KCJTb21ldGluZyB3ZW50IHdyb25nXG4iKX0NCiAgICBhPC0oYSpjKSUlbQ0KICAgIGI8LShiKmMrZCklJW0NCiAgICBpPC1pKzF9DQogIGMoYSxiKX0NCg0KYGBgDQoNCg0KRm9yIGEgZGVjayBzaXplIG9mIDEwMDA3LCB0aGlzIGlzIHRoZSBhbnN3ZXI6DQpgYGB7cn0NCm1vZHRyYW5zZm9ybSgxMDAwNyxpbnB1dCkNCiMjIw0KYGBgDQpUaGUgQSBpcyA5MzkwLiAgVGhlIEIgaXMgNzQ1OS4NCg0KRm9yIGEgc2FuaXR5IGNoZWNrIC0gdGhpcyBpcyB3aGF0IHRoZSBvbGQgbWV0aG9kIGdpdmVzIGZvciB0aGUgcG9zaXRpb24gb2YgY2FyZCAyMDE5IHRoZSBmaXJzdCAxMCBzaHVmZmxlcyBvZiBhIDEwMDA3IGNhcmQgZGVjaw0KDQpgYGB7cn0NCnAxPC0wOjEwMDA2DQp4PC0yMDE5DQpvbGRtZXRob2Q8LWMoKQ0KZm9yKGkgaW4gMToxMCl7DQogIHAxPC1zaHVmZmxlcGFydDEocDEsaW5wdXQpDQogIHg8LXdoaWNoKHAxPT0yMDE5KS0xDQogIG9sZG1ldGhvZDwtYyhvbGRtZXRob2QseCkNCn0NCm9sZG1ldGhvZA0KYGBgDQoNCg0KQW5kIHRoaXMgaXMgd2hhdCB0aGUgbmV3IG1ldGhvZCBnaXZlcw0KYGBge3J9DQpjcmQ8LTIwMTkNCm5ld21ldGhvZDwtYygpDQpmb3IoaSBpbiAxOjEwKXsNCmNyZDwtKDkzOTAqY3JkKzc0NTkpJSUxMDAwNw0KbmV3bWV0aG9kPC1jKG5ld21ldGhvZCxjcmQpfQ0KbmV3bWV0aG9kDQoNCmNhdCgiY29tcGFyZSBvbGQgbWV0aG9kIGFuZCBuZXcgbWV0aG9kOiAiLCBvbGRtZXRob2Q9PW5ld21ldGhvZCwiXG4iKQ0KDQoNCmBgYA0KVGhleSBhcmUgdGhlIHNhbWUgLSBzbyBJIHRoaW5rIHRoYXQgdGhlIG1vZHVsYXIgaXMgd29ya2luZy4NCg0KDQpUaGlzIGlzIGp1c3QgdGhlIGZ1bmN0aW9uIGZvciBjb21wb3NpbmcgQXgrQiBvbnRvIEN4K0QgbW9kIE0NCmBgYHtyfQ0KY21wczwtZnVuY3Rpb24oYSxiLGMsZCxtKXsNCiAgYTwtKGEqYyklJW0NCiAgYjwtKChiKmMpK2QpJSVtDQogIGMoYSxiKX0NCmBgYA0KDQoNClRoaXMgdGFrZXMgdGhlIGZ1bmN0aW9uIEF4K0IgYW5kIHJhaXNlcyBpdCB0byB0aGUgayAoc2h1ZmZsZXMgbXVsdGlwbGUgdGltZXMpDQpgYGB7cn0NCm11bHRpc2h1ZmZsZTwtZnVuY3Rpb24oYSxiLGssZGVja3NpemUsdCl7DQogIGM8LTENCiAgZDwtMA0KICB3aGlsZShrPjApew0KICAgIGlmKGslJTI9PTEpew0KICAgICAgZzwtY21wcyhhLGIsYyxkLGRlY2tzaXplKQ0KICAgICAgYzwtZ1sxXQ0KICAgICAgZDwtZ1syXX0NCiAgICBrPC1rJS8lMg0KICBmPC1jbXBzKGEsYixhLGIsZGVja3NpemUpDQogIGE8LWZbMV0NCiAgYjwtZlsyXX0NCiAgKGMqdCtkKSUlZGVja3NpemV9DQoNCmBgYA0KDQoNCkFuZCBhZ2FpbiwgdXNpbmcgMTAwMDcgYW5kIGNhcmQgMjAxOSAtIHRoaXMgaXMgd2hlcmUgaXQgZ29lcyBmb3IgdGhlIGZpcnN0IDEwIHNodWZmbGVzDQpgYGB7cn0NCg0KbXVsdGlzaHVmZmxldmVyc2lvbjwtYygpDQpmb3IoaSBpbiAxOjEwKXttdWx0aXNodWZmbGV2ZXJzaW9uPC1jKG11bHRpc2h1ZmZsZXZlcnNpb24sbXVsdGlzaHVmZmxlKDkzOTAsNzQ1OSxpLDEwMDA3LDIwMTkpKX0NCg0KbXVsdGlzaHVmZmxldmVyc2lvbg0KDQpjYXQoImNvbXBhcmUgbmV3IG1ldGhvZCAmIG11bHRpc2h1ZmZsZTogIiwgbXVsdGlzaHVmZmxldmVyc2lvbj09bmV3bWV0aG9kLCJcbiIpDQoNCmBgYA0KU28gdGhpcyBnaXZlcyB0aGUgc2FtZSBhbnN3ZXINCg0KQlVUISEhISEhISEhIQ0KDQpUaGUgcXVlc3Rpb24gYXNrcyBmb3IgdGhlIGNhcmQgaW4gcG9zaXRpb24gMjAyMCwgbm90IHdoZXJlIDIwMjAgZW5kcyB1cC4gIFNvLCBpdCBuZWVkcyB0aGUgaW52ZXJzZSBmdW5jdGlvbg0KDQpBbmQgdG8gZmluZCB0aGF0LCB5b3UgZmluZCB0aGUgaW52ZXJzZSBvZiBBIGZvciB0aGF0IGRlY2sgc2l6ZQ0KDQpgYGB7cn0NCiMjIyBBDQptb2RpbnYoOTM5MCwxMDAwNykNCiMjIyBCDQoobW9kaW52KDkzOTAsMTAwMDcpKi03NDU5KSUlMTAwMDcNCmBgYA0KU28sIHRvIGZpbmQgd2hhdCBjYXJkIHdhcyBpbiB0aGF0IHBvc2l0aW9uLCB5b3UgY2FuIHVzZSA1MTkgJiA4NTE5ICh3aXRoIHRoZXNlIGluc3RydWN0aW9ucyBvbiBhIGRlY2sgb2YgMTAwMDcpDQpUbyBzaG93IHRoYXQgbWFrZXMgc2Vuc2UsIHRyeSBpdCBvbiB0aGUgZmlyc3QgMTAgc2h1ZmZsZSBwb3NpdGlvbnMgLSBhbGwgb2YgdGhlbSBzaG91bGQgcmV0dXJuIDIwMTk6DQoNCmBgYHtyfQ0Kc2FwcGx5KDE6MTAsZnVuY3Rpb24oeCl7bXVsdGlzaHVmZmxlKDUxOSwxNDg4LHgsMTAwMDcsbXVsdGlzaHVmZmxldmVyc2lvblt4XSl9KQ0KYGBgDQpgYGB7cn0NCm11bHRpc2h1ZmZsZSg1MTksMTQ4OCwxMCwxMDAwNywyMDIwKQ0KYGBgDQoNCg0KRXhhY3RseSBhcyBleHBlY3RlZC4NCg0KLS0tLQ0KDQpTbyB0cnlpbmcgdGhpcyB3aXRoIGEgcmVhbGx5LCByZWFsbHksIHJlYWxseSBiaWcgZGVjayBhbmQgYSBsb3Qgb2Ygc2h1ZmZsZXM6DQoNCmBgYHtyfQ0KbGFyZ2VkZWNrPC1hcy5iaWd6KCIxMTkzMTU3MTc1MTQwNDciKQ0KbGFyZ2VzaHVmZmxlPC1hcy5iaWd6KCIxMDE3NDE1ODIwNzY2NjEiKQ0KYGBgDQoNCg0KYGBge3J9DQpBQjwtbW9kdHJhbnNmb3JtKGxhcmdlZGVjayxpbnB1dCkNCkE8LWFzLmJpZ3ooQUJbMV0pDQpCPC1hcy5iaWd6KEFCWzJdKQ0KQQ0KQg0KDQpgYGANCkE9Mjg0ODA0NDQ5NzQ3NjMNCkI9MTA0MzQzMDI5MDgyNTUzDQoNCg0KSW52ZXJ0IHRoZSBBICYgQjoNCmBgYHtyfQ0KIyMjIEENCiNpQTwtbW9kaW52KEEsbGFyZ2VkZWNrKQ0KIyMjIEINCiNpQjwtKG1vZGludihBLGxhcmdlZGVjaykqLUIpJSVsYXJnZURlY2sNCg0KYGBgDQpVc2luZyB3b2xmcmFtIGFscGhhIC0gaUEgPSA4MjY3NTk0ODcxOTkzMQ0KaUIgPSA0MDMxODM1Nzg3NjgNCg0KDQpgYGB7cn0NCmlBPC1hcy5iaWd6KCI4MjY3NTk0ODcxOTkzMSIpDQppQjwtYXMuYmlneigiNDAzMTgzNTc4NzY4IikNCmBgYA0KDQoNCkFuZCBydW4gdGhhdCB0aHJvdWdoIG11bHRpc2h1ZmZsZToNCg0KYGBge3J9DQptdWx0aXNodWZmbGUoaUEsaUIsbGFyZ2VzaHVmZmxlLGxhcmdlZGVjaywyMDIwKQ0KYGBgDQoNCg0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KU28sIHRyeWluZyB0aGlzIGJhY2t3YXJkczoNCg0KVGhpcyBmdW5jdGlvbiBkb2VzIHRoZSByZXZlcnNlIG9mIGV2ZXJ5dGhpbmcuICBJdCBjdXRzIGJhY2t3YXJkcywgaXQgcmV2ZXJzZXMgKHdoaWNoIGlzIHRoZSBzYW1lIGVpdGhlciB3YXkpLCBpdCBkZWFscyBpbnRvIGJhY2t3YXJkcy4NCg0KDQoNCg0KDQpgYGB7cn0NCmludmVyc3RyYW5zPC1mdW5jdGlvbihkZWNrc2l6ZSxpbnN0cnVjdGlvbnMpew0KICBhPC0xDQogIGI8LTANCiAgbTwtZGVja3NpemUNCiAgaTwtMQ0KICB3aGlsZShpPD1sZW5ndGgoaW5zdHJ1Y3Rpb25zKSl7DQogICAgaW5zdDwtdW5saXN0KHN0cl9zcGxpdChpbnN0cnVjdGlvbnNbaV0sIiAiKSkNCiAgICBpZihpbnN0WzFdPT0iY3V0Iil7DQogICAgICBjPC0xDQogICAgICBkPC0gLWFzLm51bWVyaWMoaW5zdFsyXSkNCiAgICB9ZWxzZSBpZihpbnN0WzJdPT0iaW50byIpew0KICAgICAgYzwtIC0xDQogICAgICBkPC0gLTENCiAgICB9ZWxzZSBpZihpbnN0WzJdPT0id2l0aCIpew0KICAgICAgYzwtIGFzLm51bWVyaWMoaW5zdFs0XSkNCiAgICAgIGQ8LSAwDQogICAgfWVsc2V7Y2F0KCJTb21ldGluZyB3ZW50IHdyb25nXG4iKX0NCiAgICAjIyMgaW52ZXJ0IGl0DQogICAgaW52YzwtbW9kaW52KGMsbSklJW0NCiAgICBpbnZkPC0gKC1kKm1vZGludihjLG0pKSUlbQ0KICAgICMjIw0KICAgIGE8LShhKmludmMpJSVtDQogICAgYjwtKGIqaW52YytpbnZkKSUlbQ0KICAgIGk8LWkrMX0NCiAgIyMjIHJlYWxpZ25zIHRvIGJlIDAgaW5kZXhlZA0KICBjKGEsYil9DQpgYGANCg0KQWdhaW4gc2hvd2luZyB0aGF0IGl0IHdvcmtzIHdpdGggMTAwMDcNCg0KYGBge3J9DQppbnZlcnN0cmFucygxMDAwNyxyZXYoaW5wdXQpKQ0KYGBgDQpBbmQgdGhvc2UgbnVtYmVycyBtYXkgbG9vayBmYW1pbGlhciAtIGJlY2F1c2UgdGhleSdyZSB0aGUgc2FtZSBhcyBhYm92ZS4gIEFuZCBzbyBpdCdzIGNsZWFyIHRoYXQgdGhpcyBzaG91bGQgd29yaw0KDQoNClRoaXMgZG9lc24ndCB3b3JrIG9uIHRoZSByZWFsbHkgYmlnIGRlY2sgLSBiZWNhdXNlIEkgY2FuJ3QgcnVuIGJpZ3ogdGhyb3VnaCBtb2RpbnYNCg==