// me_optimal.cpp
// Compile: g++ -std=c++14 me_optimal.cpp -O2 -o me_optimal.exe
#include <bits/stdc++.h>
using namespace std;
struct Pos { int r,c; };
inline bool operator==(Pos a, Pos b){ return a.r==b.r && a.c==b.c; }
inline bool operator!=(Pos a, Pos b){ return !(a==b); }
int R=16, C=16;
vector<string> grid;
Pos myPos, oppPos, goalA, goalB;
vector<vector<bool>> isDeadCorner;
vector<Pos> boxes;
int dr[4]={-1,1,0,0}, dc[4]={0,0,-1,1};
char mvChar[4]={'U','D','L','R'};
bool inb(int r,int c){ return r>=0 && r<R && c>=0 && c<C; }
bool isWall(int r,int c){
// be careful !!!!! if we can go into grid with 'b'
// if (grid[r][c] == 'b') return false;
return !inb(r,c) || grid[r][c]=='#';
}
int manhattan(const Pos &a, const Pos &b){ return abs(a.r-b.r)+abs(a.c-b.c); }
void computeDeadCorners(){
isDeadCorner.assign(R, vector<bool>(C,false));
for(int r=0;r<R;r++){
for(int c=0;c<C;c++){
if(grid[r][c]=='#') continue;
if(Pos{r,c}==goalA || Pos{r,c}==goalB) continue;
bool up = isWall(r-1,c);
bool down = isWall(r+1,c);
bool left = isWall(r,c-1);
bool right = isWall(r,c+1);
if((up && left) || (up && right) || (down && left) || (down && right))
isDeadCorner[r][c] = true;
}
}
}
// Pack state (pr,pc,br,bc,pushedFlag) into uint32_t key
inline uint32_t packKey(int pr,int pc,int br,int bc,int pushed){
// fields: 6 bits each (0..63) plenty for 16
return (uint32_t)pr | ((uint32_t)pc<<6) | ((uint32_t)br<<12) | ((uint32_t)bc<<18) | ((uint32_t)pushed<<24);
}
void unpackKey(uint32_t k,int &pr,int &pc,int &br,int &bc,int &pushed){
pr = k & 63;
pc = (k>>6) & 63;
br = (k>>12) & 63;
bc = (k>>18) & 63;
pushed = (k>>24) & 1;
}
struct Node {
int pr,pc,br,bc;
int g,h;
bool operator<(Node const& o) const {
return g+h > o.g+o.h; // min-heap via priority_queue
}
};
string aStar_withPushed(const Pos &startP, const Pos &startB, const vector<vector<char>> &boxMap){
// A* over (player, box, pushedLast)
priority_queue<Node> pq;
unordered_map<uint32_t, uint32_t> parent; // child -> parent key
unordered_map<uint32_t, char> moveTaken; // child -> move char
unordered_set<uint32_t> visited;
auto heuristic = [&](int br,int bc,int pr,int pc){
// stronger heuristic: box->goal * 3 + player->box
return manhattan(Pos{br,bc}, goalA) * 3 + manhattan(Pos{pr,pc}, Pos{br,bc});
};
int initH = heuristic(startB.r,startB.c,startP.r,startP.c);
pq.push({startP.r, startP.c, startB.r, startB.c, 0, initH});
uint32_t initKey = packKey(startP.r,startP.c,startB.r,startB.c,0);
visited.insert(initKey);
parent[initKey] = initKey; // root
while(!pq.empty()){
Node cur = pq.top(); pq.pop();
uint32_t curKey = packKey(cur.pr,cur.pc,cur.br,cur.bc,0); // pushed flag not stored in node struct; we must reconstruct pushed flag by parent map?
// Actually we need explicit pushed flag per node; adjust: store pushed in key and Node not store it. Let's redo.
// break (we won't reach here, placeholder)
break;
}
// Because above pattern needs pushed flag, implement proper loop with explicit pushed flag in key
// Re-implement using open-set priority queue of tuples
struct PQItem { int pr,pc,br,bc; int g,h; int pushed; };
struct PQCmp { bool operator()(PQItem const& a, PQItem const& b) const { return a.g + a.h > b.g + b.h; } };
priority_queue<PQItem, vector<PQItem>, PQCmp> open;
open.push({startP.r,startP.c,startB.r,startB.c,0,heuristic(startB.r,startB.c,startP.r,startP.c),0});
uint32_t startKey = packKey(startP.r,startP.c,startB.r,startB.c,0);
parent[startKey] = startKey;
moveTaken[startKey] = 'S';
visited.clear();
visited.insert(startKey);
while(!open.empty()){
PQItem cur = open.top(); open.pop();
uint32_t curPacked = packKey(cur.pr,cur.pc,cur.br,cur.bc,cur.pushed);
// If reached goal
if(cur.br == goalA.r && cur.bc == goalA.c){
// reconstruct path by following parent until startKey
string path;
uint32_t k = curPacked;
while(parent[k] != k){
char mv = moveTaken[k];
path.push_back(mv);
k = parent[k];
}
reverse(path.begin(), path.end());
return path;
}
// Expand neighbors
for(int k=0;k<4;k++){
int npr = cur.pr + dr[k];
int npc = cur.pc + dc[k];
if(!inb(npr,npc)) continue;
if(grid[npr][npc] == '#') continue;
// walking into other boxes not allowed
if(!(npr==cur.br && npc==cur.bc) && boxMap[npr][npc]) continue;
// If stepping into box cell => attempt push
if(npr==cur.br && npc==cur.bc){
// if we just pushed in previous move, do not allow push again (prevent consecutive pushes)
if(cur.pushed) continue;
int nbr = cur.br + dr[k];
int nbc = cur.bc + dc[k];
if(!inb(nbr,nbc)) continue;
if(grid[nbr][nbc] == '#') continue;
if(boxMap[nbr][nbc]) continue; // cannot push into another box
// cannot push into opponent goal
if(nbr==goalB.r && nbc==goalB.c) continue;
// avoid dead corner unless it's our goal
if(isDeadCorner[nbr][nbc] && !(nbr==goalA.r && nbc==goalA.c)) continue;
uint32_t nk = packKey(npr,npc,nbr,nbc,1);
if(visited.count(nk)) continue;
visited.insert(nk);
int ng = cur.g + 1;
int nh = manhattan(Pos{nbr,nbc}, goalA)*3 + manhattan(Pos{npr,npc}, Pos{nbr,nbc});
parent[nk] = curPacked;
moveTaken[nk] = mvChar[k];
open.push({npr,npc,nbr,nbc,ng,nh,1});
} else {
// just move (no push)
uint32_t nk = packKey(npr,npc,cur.br,cur.bc,0);
if(visited.count(nk)) continue;
visited.insert(nk);
int ng = cur.g + 1;
int nh = manhattan(Pos{cur.br,cur.bc}, goalA)*3 + manhattan(Pos{npr,npc}, Pos{cur.br,cur.bc});
parent[nk] = curPacked;
moveTaken[nk] = mvChar[k];
open.push({npr,npc,cur.br,cur.bc,ng,nh,0});
}
}
}
return "";
}
string aStar_noPushed(const Pos &startP, const Pos &startB, const vector<vector<char>> &boxMap){
// fallback typical A* (no pushed state)
// implement quick A* similar but without pushed flag
struct Item { int pr,pc,br,bc; int g,h; };
struct Cmp { bool operator()(Item const& a, Item const& b) const { return a.g+a.h > b.g+b.h; } };
priority_queue<Item, vector<Item>, Cmp> open;
unordered_set<uint32_t> visited;
unordered_map<uint32_t,uint32_t> parent;
unordered_map<uint32_t,char> moveTaken;
auto pack2 = [&](int pr,int pc,int br,int bc)->uint32_t{
return packKey(pr,pc,br,bc,0);
};
open.push({startP.r,startP.c,startB.r,startB.c,0, manhattan(startB,goalA)*3 });
uint32_t sK = pack2(startP.r,startP.c,startB.r,startB.c);
parent[sK] = sK;
visited.insert(sK);
while(!open.empty()){
Item cur = open.top(); open.pop();
if(cur.br == goalA.r && cur.bc == goalA.c){
string path;
uint32_t k = pack2(cur.pr,cur.pc,cur.br,cur.bc);
while(parent[k] != k){
path.push_back(moveTaken[k]);
k = parent[k];
}
reverse(path.begin(), path.end());
return path;
}
for(int k=0;k<4;k++){
int npr = cur.pr + dr[k], npc = cur.pc + dc[k];
if(!inb(npr,npc) || grid[npr][npc]=='#') continue;
if(!(npr==cur.br && npc==cur.bc) && boxMap[npr][npc]) continue;
// push case
if(npr==cur.br && npc==cur.bc){
int nbr = cur.br + dr[k], nbc = cur.bc + dc[k];
if(!inb(nbr,nbc) || grid[nbr][nbc]=='#') continue;
if(boxMap[nbr][nbc]) continue;
if(nbr==goalB.r && nbc==goalB.c) continue;
if(isDeadCorner[nbr][nbc] && !(nbr==goalA.r && nbc==goalA.c)) continue;
uint32_t nk = pack2(npr,npc,nbr,nbc);
if(visited.count(nk)) continue;
visited.insert(nk);
parent[nk] = pack2(cur.pr,cur.pc,cur.br,cur.bc);
moveTaken[nk] = mvChar[k];
open.push({npr,npc,nbr,nbc,cur.g+1, manhattan(Pos{nbr,nbc},goalA)*3});
} else {
uint32_t nk = pack2(npr,npc,cur.br,cur.bc);
if(visited.count(nk)) continue;
visited.insert(nk);
parent[nk] = pack2(cur.pr,cur.pc,cur.br,cur.bc);
moveTaken[nk] = mvChar[k];
open.push({npr,npc,cur.br,cur.bc,cur.g+1, manhattan(Pos{cur.br,cur.bc},goalA)*3 + manhattan(Pos{npr,npc}, Pos{cur.br,cur.bc})});
}
}
}
return "";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Read input
if(!(cin >> R)) { cout << "S\n"; return 0; }
grid.resize(R);
for(int i=0;i<R;i++){
cin >> grid[i];
}
cin >> myPos.r >> myPos.c;
cin >> oppPos.r >> oppPos.c;
// find goals and boxes
boxes.clear();
for(int r=0;r<R;r++) for(int c=0;c<C;c++){
if(grid[r][c]=='A') goalA = {r,c};
if(grid[r][c]=='B') goalB = {r,c};
if(grid[r][c]=='X') boxes.push_back({r,c});
}
// precompute dead corners
computeDeadCorners();
// build box map for fast checks
vector<vector<char>> boxMap(R, vector<char>(C,0));
for(auto &b: boxes) boxMap[b.r][b.c] = 1;
// filter candidate boxes: not dead corner & small Manhattan thresholds
vector<pair<int,Pos>> cand;
for(auto &b: boxes){
if(isDeadCorner[b.r][b.c] && !(b==goalA)) continue;
int d = manhattan(myPos,b);
// loosen threshold a bit for optimal variant
if(d > 50) continue;
cand.push_back({d,b});
}
sort(cand.begin(), cand.end(), [](auto &a, auto &b){ return a.first < b.first; });
string bestPath="";
// Try each candidate with the better planner (pushed flag)
for(auto &pr: cand){
Pos box = pr.second;
// run A* with pushed-in-state
string path = aStar_withPushed(myPos, box, boxMap);
if(!path.empty()){
bestPath = path;
break;
}
}
// fallback: try faster A* if optimal didn't find anything
if(bestPath.empty()){
for(auto &pr: cand){
Pos box = pr.second;
string path = aStar_noPushed(myPos, box, boxMap);
if(!path.empty()){
bestPath = path;
break;
}
}
}
if(!bestPath.empty()){
cout << bestPath[0] << "\n";
return 0;
}
// no plan -> random legal move
vector<int> dirs = {0,1,2,3};
random_shuffle(dirs.begin(), dirs.end());
for(int k: dirs){
int nr = myPos.r + dr[k], nc = myPos.c + dc[k];
if(inb(nr,nc) && grid[nr][nc] != '#' && !boxMap[nr][nc]){
cout << mvChar[k] << "\n";
return 0;
}
}
cout << "S\n";
return 0;
}
Ly8gbWVfb3B0aW1hbC5jcHAKLy8gQ29tcGlsZTogZysrIC1zdGQ9YysrMTQgbWVfb3B0aW1hbC5jcHAgLU8yIC1vIG1lX29wdGltYWwuZXhlCiNpbmNsdWRlIDxiaXRzL3N0ZGMrKy5oPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKc3RydWN0IFBvcyB7IGludCByLGM7IH07CmlubGluZSBib29sIG9wZXJhdG9yPT0oUG9zIGEsIFBvcyBiKXsgcmV0dXJuIGEucj09Yi5yICYmIGEuYz09Yi5jOyB9CmlubGluZSBib29sIG9wZXJhdG9yIT0oUG9zIGEsIFBvcyBiKXsgcmV0dXJuICEoYT09Yik7IH0KCmludCBSPTE2LCBDPTE2Owp2ZWN0b3I8c3RyaW5nPiBncmlkOwpQb3MgbXlQb3MsIG9wcFBvcywgZ29hbEEsIGdvYWxCOwp2ZWN0b3I8dmVjdG9yPGJvb2w+PiBpc0RlYWRDb3JuZXI7CnZlY3RvcjxQb3M+IGJveGVzOwoKaW50IGRyWzRdPXstMSwxLDAsMH0sIGRjWzRdPXswLDAsLTEsMX07CmNoYXIgbXZDaGFyWzRdPXsnVScsJ0QnLCdMJywnUid9OwoKYm9vbCBpbmIoaW50IHIsaW50IGMpeyByZXR1cm4gcj49MCAmJiByPFIgJiYgYz49MCAmJiBjPEM7IH0KYm9vbCBpc1dhbGwoaW50IHIsaW50IGMpeyAKCS8vIGJlIGNhcmVmdWwgISEhISEgaWYgd2UgY2FuIGdvIGludG8gZ3JpZCB3aXRoICdiJwoJLy8gaWYgKGdyaWRbcl1bY10gPT0gJ2InKSByZXR1cm4gZmFsc2U7CglyZXR1cm4gIWluYihyLGMpIHx8IGdyaWRbcl1bY109PScjJzsgCn0KaW50IG1hbmhhdHRhbihjb25zdCBQb3MgJmEsIGNvbnN0IFBvcyAmYil7IHJldHVybiBhYnMoYS5yLWIucikrYWJzKGEuYy1iLmMpOyB9Cgp2b2lkIGNvbXB1dGVEZWFkQ29ybmVycygpewogICAgaXNEZWFkQ29ybmVyLmFzc2lnbihSLCB2ZWN0b3I8Ym9vbD4oQyxmYWxzZSkpOwogICAgZm9yKGludCByPTA7cjxSO3IrKyl7CiAgICAgICAgZm9yKGludCBjPTA7YzxDO2MrKyl7CiAgICAgICAgICAgIGlmKGdyaWRbcl1bY109PScjJykgY29udGludWU7CiAgICAgICAgICAgIGlmKFBvc3tyLGN9PT1nb2FsQSB8fCBQb3N7cixjfT09Z29hbEIpIGNvbnRpbnVlOwogICAgICAgICAgICBib29sIHVwID0gaXNXYWxsKHItMSxjKTsKICAgICAgICAgICAgYm9vbCBkb3duID0gaXNXYWxsKHIrMSxjKTsKICAgICAgICAgICAgYm9vbCBsZWZ0ID0gaXNXYWxsKHIsYy0xKTsKICAgICAgICAgICAgYm9vbCByaWdodCA9IGlzV2FsbChyLGMrMSk7CiAgICAgICAgICAgIGlmKCh1cCAmJiBsZWZ0KSB8fCAodXAgJiYgcmlnaHQpIHx8IChkb3duICYmIGxlZnQpIHx8IChkb3duICYmIHJpZ2h0KSkKICAgICAgICAgICAgICAgIGlzRGVhZENvcm5lcltyXVtjXSA9IHRydWU7CiAgICAgICAgfQogICAgfQp9CgovLyBQYWNrIHN0YXRlIChwcixwYyxicixiYyxwdXNoZWRGbGFnKSBpbnRvIHVpbnQzMl90IGtleQppbmxpbmUgdWludDMyX3QgcGFja0tleShpbnQgcHIsaW50IHBjLGludCBicixpbnQgYmMsaW50IHB1c2hlZCl7CiAgICAvLyBmaWVsZHM6IDYgYml0cyBlYWNoICgwLi42MykgcGxlbnR5IGZvciAxNgogICAgcmV0dXJuICh1aW50MzJfdClwciB8ICgodWludDMyX3QpcGM8PDYpIHwgKCh1aW50MzJfdClicjw8MTIpIHwgKCh1aW50MzJfdCliYzw8MTgpIHwgKCh1aW50MzJfdClwdXNoZWQ8PDI0KTsKfQp2b2lkIHVucGFja0tleSh1aW50MzJfdCBrLGludCAmcHIsaW50ICZwYyxpbnQgJmJyLGludCAmYmMsaW50ICZwdXNoZWQpewogICAgcHIgPSBrICYgNjM7CiAgICBwYyA9IChrPj42KSAmIDYzOwogICAgYnIgPSAoaz4+MTIpICYgNjM7CiAgICBiYyA9IChrPj4xOCkgJiA2MzsKICAgIHB1c2hlZCA9IChrPj4yNCkgJiAxOwp9CgpzdHJ1Y3QgTm9kZSB7CiAgICBpbnQgcHIscGMsYnIsYmM7CiAgICBpbnQgZyxoOwogICAgYm9vbCBvcGVyYXRvcjwoTm9kZSBjb25zdCYgbykgY29uc3QgewogICAgICAgIHJldHVybiBnK2ggPiBvLmcrby5oOyAvLyBtaW4taGVhcCB2aWEgcHJpb3JpdHlfcXVldWUKICAgIH0KfTsKCnN0cmluZyBhU3Rhcl93aXRoUHVzaGVkKGNvbnN0IFBvcyAmc3RhcnRQLCBjb25zdCBQb3MgJnN0YXJ0QiwgY29uc3QgdmVjdG9yPHZlY3RvcjxjaGFyPj4gJmJveE1hcCl7CiAgICAvLyBBKiBvdmVyIChwbGF5ZXIsIGJveCwgcHVzaGVkTGFzdCkKICAgIHByaW9yaXR5X3F1ZXVlPE5vZGU+IHBxOwogICAgdW5vcmRlcmVkX21hcDx1aW50MzJfdCwgdWludDMyX3Q+IHBhcmVudDsgLy8gY2hpbGQgLT4gcGFyZW50IGtleQogICAgdW5vcmRlcmVkX21hcDx1aW50MzJfdCwgY2hhcj4gbW92ZVRha2VuOyAgLy8gY2hpbGQgLT4gbW92ZSBjaGFyCiAgICB1bm9yZGVyZWRfc2V0PHVpbnQzMl90PiB2aXNpdGVkOwoKICAgIGF1dG8gaGV1cmlzdGljID0gWyZdKGludCBicixpbnQgYmMsaW50IHByLGludCBwYyl7CiAgICAgICAgLy8gc3Ryb25nZXIgaGV1cmlzdGljOiBib3gtPmdvYWwgKiAzICsgcGxheWVyLT5ib3gKICAgICAgICByZXR1cm4gbWFuaGF0dGFuKFBvc3ticixiY30sIGdvYWxBKSAqIDMgKyBtYW5oYXR0YW4oUG9ze3ByLHBjfSwgUG9ze2JyLGJjfSk7CiAgICB9OwoKICAgIGludCBpbml0SCA9IGhldXJpc3RpYyhzdGFydEIucixzdGFydEIuYyxzdGFydFAucixzdGFydFAuYyk7CiAgICBwcS5wdXNoKHtzdGFydFAuciwgc3RhcnRQLmMsIHN0YXJ0Qi5yLCBzdGFydEIuYywgMCwgaW5pdEh9KTsKICAgIHVpbnQzMl90IGluaXRLZXkgPSBwYWNrS2V5KHN0YXJ0UC5yLHN0YXJ0UC5jLHN0YXJ0Qi5yLHN0YXJ0Qi5jLDApOwogICAgdmlzaXRlZC5pbnNlcnQoaW5pdEtleSk7CiAgICBwYXJlbnRbaW5pdEtleV0gPSBpbml0S2V5OyAvLyByb290CgogICAgd2hpbGUoIXBxLmVtcHR5KCkpewogICAgICAgIE5vZGUgY3VyID0gcHEudG9wKCk7IHBxLnBvcCgpOwogICAgICAgIHVpbnQzMl90IGN1cktleSA9IHBhY2tLZXkoY3VyLnByLGN1ci5wYyxjdXIuYnIsY3VyLmJjLDApOyAvLyBwdXNoZWQgZmxhZyBub3Qgc3RvcmVkIGluIG5vZGUgc3RydWN0OyB3ZSBtdXN0IHJlY29uc3RydWN0IHB1c2hlZCBmbGFnIGJ5IHBhcmVudCBtYXA/CiAgICAgICAgLy8gQWN0dWFsbHkgd2UgbmVlZCBleHBsaWNpdCBwdXNoZWQgZmxhZyBwZXIgbm9kZTsgYWRqdXN0OiBzdG9yZSBwdXNoZWQgaW4ga2V5IGFuZCBOb2RlIG5vdCBzdG9yZSBpdC4gTGV0J3MgcmVkby4KCiAgICAgICAgLy8gYnJlYWsgKHdlIHdvbid0IHJlYWNoIGhlcmUsIHBsYWNlaG9sZGVyKQogICAgICAgIGJyZWFrOwogICAgfQoKICAgIC8vIEJlY2F1c2UgYWJvdmUgcGF0dGVybiBuZWVkcyBwdXNoZWQgZmxhZywgaW1wbGVtZW50IHByb3BlciBsb29wIHdpdGggZXhwbGljaXQgcHVzaGVkIGZsYWcgaW4ga2V5CiAgICAvLyBSZS1pbXBsZW1lbnQgdXNpbmcgb3Blbi1zZXQgcHJpb3JpdHkgcXVldWUgb2YgdHVwbGVzCiAgICBzdHJ1Y3QgUFFJdGVtIHsgaW50IHByLHBjLGJyLGJjOyBpbnQgZyxoOyBpbnQgcHVzaGVkOyB9OwogICAgc3RydWN0IFBRQ21wIHsgYm9vbCBvcGVyYXRvcigpKFBRSXRlbSBjb25zdCYgYSwgUFFJdGVtIGNvbnN0JiBiKSBjb25zdCB7IHJldHVybiBhLmcgKyBhLmggPiBiLmcgKyBiLmg7IH0gfTsKICAgIHByaW9yaXR5X3F1ZXVlPFBRSXRlbSwgdmVjdG9yPFBRSXRlbT4sIFBRQ21wPiBvcGVuOwogICAgb3Blbi5wdXNoKHtzdGFydFAucixzdGFydFAuYyxzdGFydEIucixzdGFydEIuYywwLGhldXJpc3RpYyhzdGFydEIucixzdGFydEIuYyxzdGFydFAucixzdGFydFAuYyksMH0pOwogICAgdWludDMyX3Qgc3RhcnRLZXkgPSBwYWNrS2V5KHN0YXJ0UC5yLHN0YXJ0UC5jLHN0YXJ0Qi5yLHN0YXJ0Qi5jLDApOwogICAgcGFyZW50W3N0YXJ0S2V5XSA9IHN0YXJ0S2V5OwogICAgbW92ZVRha2VuW3N0YXJ0S2V5XSA9ICdTJzsKICAgIHZpc2l0ZWQuY2xlYXIoKTsKICAgIHZpc2l0ZWQuaW5zZXJ0KHN0YXJ0S2V5KTsKCiAgICB3aGlsZSghb3Blbi5lbXB0eSgpKXsKICAgICAgICBQUUl0ZW0gY3VyID0gb3Blbi50b3AoKTsgb3Blbi5wb3AoKTsKICAgICAgICB1aW50MzJfdCBjdXJQYWNrZWQgPSBwYWNrS2V5KGN1ci5wcixjdXIucGMsY3VyLmJyLGN1ci5iYyxjdXIucHVzaGVkKTsKICAgICAgICAvLyBJZiByZWFjaGVkIGdvYWwKICAgICAgICBpZihjdXIuYnIgPT0gZ29hbEEuciAmJiBjdXIuYmMgPT0gZ29hbEEuYyl7CiAgICAgICAgICAgIC8vIHJlY29uc3RydWN0IHBhdGggYnkgZm9sbG93aW5nIHBhcmVudCB1bnRpbCBzdGFydEtleQogICAgICAgICAgICBzdHJpbmcgcGF0aDsKICAgICAgICAgICAgdWludDMyX3QgayA9IGN1clBhY2tlZDsKICAgICAgICAgICAgd2hpbGUocGFyZW50W2tdICE9IGspewogICAgICAgICAgICAgICAgY2hhciBtdiA9IG1vdmVUYWtlbltrXTsKICAgICAgICAgICAgICAgIHBhdGgucHVzaF9iYWNrKG12KTsKICAgICAgICAgICAgICAgIGsgPSBwYXJlbnRba107CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV2ZXJzZShwYXRoLmJlZ2luKCksIHBhdGguZW5kKCkpOwogICAgICAgICAgICByZXR1cm4gcGF0aDsKICAgICAgICB9CiAgICAgICAgLy8gRXhwYW5kIG5laWdoYm9ycwogICAgICAgIGZvcihpbnQgaz0wO2s8NDtrKyspewogICAgICAgICAgICBpbnQgbnByID0gY3VyLnByICsgZHJba107CiAgICAgICAgICAgIGludCBucGMgPSBjdXIucGMgKyBkY1trXTsKICAgICAgICAgICAgaWYoIWluYihucHIsbnBjKSkgY29udGludWU7CiAgICAgICAgICAgIGlmKGdyaWRbbnByXVtucGNdID09ICcjJykgY29udGludWU7CiAgICAgICAgICAgIC8vIHdhbGtpbmcgaW50byBvdGhlciBib3hlcyBub3QgYWxsb3dlZAogICAgICAgICAgICBpZighKG5wcj09Y3VyLmJyICYmIG5wYz09Y3VyLmJjKSAmJiBib3hNYXBbbnByXVtucGNdKSBjb250aW51ZTsKCiAgICAgICAgICAgIC8vIElmIHN0ZXBwaW5nIGludG8gYm94IGNlbGwgPT4gYXR0ZW1wdCBwdXNoCiAgICAgICAgICAgIGlmKG5wcj09Y3VyLmJyICYmIG5wYz09Y3VyLmJjKXsKICAgICAgICAgICAgICAgIC8vIGlmIHdlIGp1c3QgcHVzaGVkIGluIHByZXZpb3VzIG1vdmUsIGRvIG5vdCBhbGxvdyBwdXNoIGFnYWluIChwcmV2ZW50IGNvbnNlY3V0aXZlIHB1c2hlcykKICAgICAgICAgICAgICAgIGlmKGN1ci5wdXNoZWQpIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgaW50IG5iciA9IGN1ci5iciArIGRyW2tdOwogICAgICAgICAgICAgICAgaW50IG5iYyA9IGN1ci5iYyArIGRjW2tdOwogICAgICAgICAgICAgICAgaWYoIWluYihuYnIsbmJjKSkgY29udGludWU7CiAgICAgICAgICAgICAgICBpZihncmlkW25icl1bbmJjXSA9PSAnIycpIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgaWYoYm94TWFwW25icl1bbmJjXSkgY29udGludWU7IC8vIGNhbm5vdCBwdXNoIGludG8gYW5vdGhlciBib3gKICAgICAgICAgICAgICAgIC8vIGNhbm5vdCBwdXNoIGludG8gb3Bwb25lbnQgZ29hbAogICAgICAgICAgICAgICAgaWYobmJyPT1nb2FsQi5yICYmIG5iYz09Z29hbEIuYykgY29udGludWU7CiAgICAgICAgICAgICAgICAvLyBhdm9pZCBkZWFkIGNvcm5lciB1bmxlc3MgaXQncyBvdXIgZ29hbAogICAgICAgICAgICAgICAgaWYoaXNEZWFkQ29ybmVyW25icl1bbmJjXSAmJiAhKG5icj09Z29hbEEuciAmJiBuYmM9PWdvYWxBLmMpKSBjb250aW51ZTsKICAgICAgICAgICAgICAgIHVpbnQzMl90IG5rID0gcGFja0tleShucHIsbnBjLG5icixuYmMsMSk7CiAgICAgICAgICAgICAgICBpZih2aXNpdGVkLmNvdW50KG5rKSkgY29udGludWU7CiAgICAgICAgICAgICAgICB2aXNpdGVkLmluc2VydChuayk7CiAgICAgICAgICAgICAgICBpbnQgbmcgPSBjdXIuZyArIDE7CiAgICAgICAgICAgICAgICBpbnQgbmggPSBtYW5oYXR0YW4oUG9ze25icixuYmN9LCBnb2FsQSkqMyArIG1hbmhhdHRhbihQb3N7bnByLG5wY30sIFBvc3tuYnIsbmJjfSk7CiAgICAgICAgICAgICAgICBwYXJlbnRbbmtdID0gY3VyUGFja2VkOwogICAgICAgICAgICAgICAgbW92ZVRha2VuW25rXSA9IG12Q2hhcltrXTsKICAgICAgICAgICAgICAgIG9wZW4ucHVzaCh7bnByLG5wYyxuYnIsbmJjLG5nLG5oLDF9KTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIC8vIGp1c3QgbW92ZSAobm8gcHVzaCkKICAgICAgICAgICAgICAgIHVpbnQzMl90IG5rID0gcGFja0tleShucHIsbnBjLGN1ci5icixjdXIuYmMsMCk7CiAgICAgICAgICAgICAgICBpZih2aXNpdGVkLmNvdW50KG5rKSkgY29udGludWU7CiAgICAgICAgICAgICAgICB2aXNpdGVkLmluc2VydChuayk7CiAgICAgICAgICAgICAgICBpbnQgbmcgPSBjdXIuZyArIDE7CiAgICAgICAgICAgICAgICBpbnQgbmggPSBtYW5oYXR0YW4oUG9ze2N1ci5icixjdXIuYmN9LCBnb2FsQSkqMyArIG1hbmhhdHRhbihQb3N7bnByLG5wY30sIFBvc3tjdXIuYnIsY3VyLmJjfSk7CiAgICAgICAgICAgICAgICBwYXJlbnRbbmtdID0gY3VyUGFja2VkOwogICAgICAgICAgICAgICAgbW92ZVRha2VuW25rXSA9IG12Q2hhcltrXTsKICAgICAgICAgICAgICAgIG9wZW4ucHVzaCh7bnByLG5wYyxjdXIuYnIsY3VyLmJjLG5nLG5oLDB9KTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KICAgIHJldHVybiAiIjsKfQoKc3RyaW5nIGFTdGFyX25vUHVzaGVkKGNvbnN0IFBvcyAmc3RhcnRQLCBjb25zdCBQb3MgJnN0YXJ0QiwgY29uc3QgdmVjdG9yPHZlY3RvcjxjaGFyPj4gJmJveE1hcCl7CiAgICAvLyBmYWxsYmFjayB0eXBpY2FsIEEqIChubyBwdXNoZWQgc3RhdGUpCiAgICAvLyBpbXBsZW1lbnQgcXVpY2sgQSogc2ltaWxhciBidXQgd2l0aG91dCBwdXNoZWQgZmxhZwogICAgc3RydWN0IEl0ZW0geyBpbnQgcHIscGMsYnIsYmM7IGludCBnLGg7IH07CiAgICBzdHJ1Y3QgQ21wIHsgYm9vbCBvcGVyYXRvcigpKEl0ZW0gY29uc3QmIGEsIEl0ZW0gY29uc3QmIGIpIGNvbnN0IHsgcmV0dXJuIGEuZythLmggPiBiLmcrYi5oOyB9IH07CiAgICBwcmlvcml0eV9xdWV1ZTxJdGVtLCB2ZWN0b3I8SXRlbT4sIENtcD4gb3BlbjsKICAgIHVub3JkZXJlZF9zZXQ8dWludDMyX3Q+IHZpc2l0ZWQ7CiAgICB1bm9yZGVyZWRfbWFwPHVpbnQzMl90LHVpbnQzMl90PiBwYXJlbnQ7CiAgICB1bm9yZGVyZWRfbWFwPHVpbnQzMl90LGNoYXI+IG1vdmVUYWtlbjsKCiAgICBhdXRvIHBhY2syID0gWyZdKGludCBwcixpbnQgcGMsaW50IGJyLGludCBiYyktPnVpbnQzMl90ewogICAgICAgIHJldHVybiBwYWNrS2V5KHByLHBjLGJyLGJjLDApOwogICAgfTsKCiAgICBvcGVuLnB1c2goe3N0YXJ0UC5yLHN0YXJ0UC5jLHN0YXJ0Qi5yLHN0YXJ0Qi5jLDAsIG1hbmhhdHRhbihzdGFydEIsZ29hbEEpKjMgfSk7CiAgICB1aW50MzJfdCBzSyA9IHBhY2syKHN0YXJ0UC5yLHN0YXJ0UC5jLHN0YXJ0Qi5yLHN0YXJ0Qi5jKTsKICAgIHBhcmVudFtzS10gPSBzSzsKICAgIHZpc2l0ZWQuaW5zZXJ0KHNLKTsKCiAgICB3aGlsZSghb3Blbi5lbXB0eSgpKXsKICAgICAgICBJdGVtIGN1ciA9IG9wZW4udG9wKCk7IG9wZW4ucG9wKCk7CiAgICAgICAgaWYoY3VyLmJyID09IGdvYWxBLnIgJiYgY3VyLmJjID09IGdvYWxBLmMpewogICAgICAgICAgICBzdHJpbmcgcGF0aDsKICAgICAgICAgICAgdWludDMyX3QgayA9IHBhY2syKGN1ci5wcixjdXIucGMsY3VyLmJyLGN1ci5iYyk7CiAgICAgICAgICAgIHdoaWxlKHBhcmVudFtrXSAhPSBrKXsKICAgICAgICAgICAgICAgIHBhdGgucHVzaF9iYWNrKG1vdmVUYWtlbltrXSk7CiAgICAgICAgICAgICAgICBrID0gcGFyZW50W2tdOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldmVyc2UocGF0aC5iZWdpbigpLCBwYXRoLmVuZCgpKTsKICAgICAgICAgICAgcmV0dXJuIHBhdGg7CiAgICAgICAgfQogICAgICAgIGZvcihpbnQgaz0wO2s8NDtrKyspewogICAgICAgICAgICBpbnQgbnByID0gY3VyLnByICsgZHJba10sIG5wYyA9IGN1ci5wYyArIGRjW2tdOwogICAgICAgICAgICBpZighaW5iKG5wcixucGMpIHx8IGdyaWRbbnByXVtucGNdPT0nIycpIGNvbnRpbnVlOwogICAgICAgICAgICBpZighKG5wcj09Y3VyLmJyICYmIG5wYz09Y3VyLmJjKSAmJiBib3hNYXBbbnByXVtucGNdKSBjb250aW51ZTsKICAgICAgICAgICAgLy8gcHVzaCBjYXNlCiAgICAgICAgICAgIGlmKG5wcj09Y3VyLmJyICYmIG5wYz09Y3VyLmJjKXsKICAgICAgICAgICAgICAgIGludCBuYnIgPSBjdXIuYnIgKyBkcltrXSwgbmJjID0gY3VyLmJjICsgZGNba107CiAgICAgICAgICAgICAgICBpZighaW5iKG5icixuYmMpIHx8IGdyaWRbbmJyXVtuYmNdPT0nIycpIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgaWYoYm94TWFwW25icl1bbmJjXSkgY29udGludWU7CiAgICAgICAgICAgICAgICBpZihuYnI9PWdvYWxCLnIgJiYgbmJjPT1nb2FsQi5jKSBjb250aW51ZTsKICAgICAgICAgICAgICAgIGlmKGlzRGVhZENvcm5lcltuYnJdW25iY10gJiYgIShuYnI9PWdvYWxBLnIgJiYgbmJjPT1nb2FsQS5jKSkgY29udGludWU7CiAgICAgICAgICAgICAgICB1aW50MzJfdCBuayA9IHBhY2syKG5wcixucGMsbmJyLG5iYyk7CiAgICAgICAgICAgICAgICBpZih2aXNpdGVkLmNvdW50KG5rKSkgY29udGludWU7CiAgICAgICAgICAgICAgICB2aXNpdGVkLmluc2VydChuayk7CiAgICAgICAgICAgICAgICBwYXJlbnRbbmtdID0gcGFjazIoY3VyLnByLGN1ci5wYyxjdXIuYnIsY3VyLmJjKTsKICAgICAgICAgICAgICAgIG1vdmVUYWtlbltua10gPSBtdkNoYXJba107CiAgICAgICAgICAgICAgICBvcGVuLnB1c2goe25wcixucGMsbmJyLG5iYyxjdXIuZysxLCBtYW5oYXR0YW4oUG9ze25icixuYmN9LGdvYWxBKSozfSk7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB1aW50MzJfdCBuayA9IHBhY2syKG5wcixucGMsY3VyLmJyLGN1ci5iYyk7CiAgICAgICAgICAgICAgICBpZih2aXNpdGVkLmNvdW50KG5rKSkgY29udGludWU7CiAgICAgICAgICAgICAgICB2aXNpdGVkLmluc2VydChuayk7CiAgICAgICAgICAgICAgICBwYXJlbnRbbmtdID0gcGFjazIoY3VyLnByLGN1ci5wYyxjdXIuYnIsY3VyLmJjKTsKICAgICAgICAgICAgICAgIG1vdmVUYWtlbltua10gPSBtdkNoYXJba107CiAgICAgICAgICAgICAgICBvcGVuLnB1c2goe25wcixucGMsY3VyLmJyLGN1ci5iYyxjdXIuZysxLCBtYW5oYXR0YW4oUG9ze2N1ci5icixjdXIuYmN9LGdvYWxBKSozICsgbWFuaGF0dGFuKFBvc3tucHIsbnBjfSwgUG9ze2N1ci5icixjdXIuYmN9KX0pOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQogICAgcmV0dXJuICIiOwp9CgppbnQgbWFpbigpewogICAgaW9zOjpzeW5jX3dpdGhfc3RkaW8oZmFsc2UpOwogICAgY2luLnRpZShudWxscHRyKTsKCiAgICAvLyBSZWFkIGlucHV0CiAgICBpZighKGNpbiA+PiBSKSkgeyBjb3V0IDw8ICJTXG4iOyByZXR1cm4gMDsgfQogICAgZ3JpZC5yZXNpemUoUik7CiAgICBmb3IoaW50IGk9MDtpPFI7aSsrKXsKICAgICAgICBjaW4gPj4gZ3JpZFtpXTsKICAgIH0KICAgIGNpbiA+PiBteVBvcy5yID4+IG15UG9zLmM7CiAgICBjaW4gPj4gb3BwUG9zLnIgPj4gb3BwUG9zLmM7CgogICAgLy8gZmluZCBnb2FscyBhbmQgYm94ZXMKICAgIGJveGVzLmNsZWFyKCk7CiAgICBmb3IoaW50IHI9MDtyPFI7cisrKSBmb3IoaW50IGM9MDtjPEM7YysrKXsKICAgICAgICBpZihncmlkW3JdW2NdPT0nQScpIGdvYWxBID0ge3IsY307CiAgICAgICAgaWYoZ3JpZFtyXVtjXT09J0InKSBnb2FsQiA9IHtyLGN9OwogICAgICAgIGlmKGdyaWRbcl1bY109PSdYJykgYm94ZXMucHVzaF9iYWNrKHtyLGN9KTsKICAgIH0KCiAgICAvLyBwcmVjb21wdXRlIGRlYWQgY29ybmVycwogICAgY29tcHV0ZURlYWRDb3JuZXJzKCk7CgogICAgLy8gYnVpbGQgYm94IG1hcCBmb3IgZmFzdCBjaGVja3MKICAgIHZlY3Rvcjx2ZWN0b3I8Y2hhcj4+IGJveE1hcChSLCB2ZWN0b3I8Y2hhcj4oQywwKSk7CiAgICBmb3IoYXV0byAmYjogYm94ZXMpIGJveE1hcFtiLnJdW2IuY10gPSAxOwoKICAgIC8vIGZpbHRlciBjYW5kaWRhdGUgYm94ZXM6IG5vdCBkZWFkIGNvcm5lciAmIHNtYWxsIE1hbmhhdHRhbiB0aHJlc2hvbGRzCiAgICB2ZWN0b3I8cGFpcjxpbnQsUG9zPj4gY2FuZDsKICAgIGZvcihhdXRvICZiOiBib3hlcyl7CiAgICAgICAgaWYoaXNEZWFkQ29ybmVyW2Iucl1bYi5jXSAmJiAhKGI9PWdvYWxBKSkgY29udGludWU7CiAgICAgICAgaW50IGQgPSBtYW5oYXR0YW4obXlQb3MsYik7CiAgICAgICAgLy8gbG9vc2VuIHRocmVzaG9sZCBhIGJpdCBmb3Igb3B0aW1hbCB2YXJpYW50CiAgICAgICAgaWYoZCA+IDUwKSBjb250aW51ZTsKICAgICAgICBjYW5kLnB1c2hfYmFjayh7ZCxifSk7CiAgICB9CiAgICBzb3J0KGNhbmQuYmVnaW4oKSwgY2FuZC5lbmQoKSwgW10oYXV0byAmYSwgYXV0byAmYil7IHJldHVybiBhLmZpcnN0IDwgYi5maXJzdDsgfSk7CgogICAgc3RyaW5nIGJlc3RQYXRoPSIiOwogICAgLy8gVHJ5IGVhY2ggY2FuZGlkYXRlIHdpdGggdGhlIGJldHRlciBwbGFubmVyIChwdXNoZWQgZmxhZykKICAgIGZvcihhdXRvICZwcjogY2FuZCl7CiAgICAgICAgUG9zIGJveCA9IHByLnNlY29uZDsKICAgICAgICAvLyBydW4gQSogd2l0aCBwdXNoZWQtaW4tc3RhdGUKICAgICAgICBzdHJpbmcgcGF0aCA9IGFTdGFyX3dpdGhQdXNoZWQobXlQb3MsIGJveCwgYm94TWFwKTsKICAgICAgICBpZighcGF0aC5lbXB0eSgpKXsKICAgICAgICAgICAgYmVzdFBhdGggPSBwYXRoOwogICAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICB9CgogICAgLy8gZmFsbGJhY2s6IHRyeSBmYXN0ZXIgQSogaWYgb3B0aW1hbCBkaWRuJ3QgZmluZCBhbnl0aGluZwogICAgaWYoYmVzdFBhdGguZW1wdHkoKSl7CiAgICAgICAgZm9yKGF1dG8gJnByOiBjYW5kKXsKICAgICAgICAgICAgUG9zIGJveCA9IHByLnNlY29uZDsKICAgICAgICAgICAgc3RyaW5nIHBhdGggPSBhU3Rhcl9ub1B1c2hlZChteVBvcywgYm94LCBib3hNYXApOwogICAgICAgICAgICBpZighcGF0aC5lbXB0eSgpKXsKICAgICAgICAgICAgICAgIGJlc3RQYXRoID0gcGF0aDsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQoKICAgIGlmKCFiZXN0UGF0aC5lbXB0eSgpKXsKICAgICAgICBjb3V0IDw8IGJlc3RQYXRoWzBdIDw8ICJcbiI7CiAgICAgICAgcmV0dXJuIDA7CiAgICB9CgogICAgLy8gbm8gcGxhbiAtPiByYW5kb20gbGVnYWwgbW92ZQogICAgdmVjdG9yPGludD4gZGlycyA9IHswLDEsMiwzfTsKICAgIHJhbmRvbV9zaHVmZmxlKGRpcnMuYmVnaW4oKSwgZGlycy5lbmQoKSk7CiAgICBmb3IoaW50IGs6IGRpcnMpewogICAgICAgIGludCBuciA9IG15UG9zLnIgKyBkcltrXSwgbmMgPSBteVBvcy5jICsgZGNba107CiAgICAgICAgaWYoaW5iKG5yLG5jKSAmJiBncmlkW25yXVtuY10gIT0gJyMnICYmICFib3hNYXBbbnJdW25jXSl7CiAgICAgICAgICAgIGNvdXQgPDwgbXZDaGFyW2tdIDw8ICJcbiI7CiAgICAgICAgICAgIHJldHVybiAwOwogICAgICAgIH0KICAgIH0KICAgIGNvdXQgPDwgIlNcbiI7CiAgICByZXR1cm4gMDsKfQo=