#include<bits/stdc++.h>
using namespace std;
// Định nghĩa kích thước lưới
#define ROW 9
#define COL 10
// Cấu trúc tọa độ (Point)
typedef pair<int, int> Pair;
// Cấu trúc lưu trữ thông tin của một ô (Node)
struct cell {
// Tọa độ cha (parent) để truy vết
// pair<int, int> parent;
int parent_i, parent_j;
// f = g + h
double f, g, h;
};
// --- Các hàm hỗ trợ ---
// Kiểm tra xem ô (row, col) có nằm trong lưới không
bool isValid(int row, int col) {
return (row >= 0) && (row < ROW) && (col >= 0) && (col < COL);
}
// Kiểm tra xem ô có phải là tường (bị chặn) không
bool isUnBlocked(const vector<vector<int>>& grid, int row, int col) {
// 1 = bị chặn, 0 = không bị chặn
return grid[row][col] == 0;
}
// Kiểm tra xem có phải là ô đích không
bool isDestination(int row, int col, Pair dest) {
return (row == dest.first && col == dest.second);
}
// Tính Heuristic (h) - dùng khoảng cách Manhattan
double calculateHValue(int row, int col, Pair dest) {
// return sqrt(pow(row - dest.first, 2) + pow(col - dest.second, 2)); // Euclidean
return abs(row - dest.first) + abs(col - dest.second); // Manhattan
}
// Truy vết đường đi từ đích về
void tracePath(const vector<vector<cell>>& cellDetails, Pair dest) {
cout << "Đường đi là: " << endl;
stack<Pair> path;
int row = dest.first;
int col = dest.second;
while (!(cellDetails[row][col].parent_i == row && cellDetails[row][col].parent_j == col)) {
path.push({row, col});
int temp_row = cellDetails[row][col].parent_i;
int temp_col = cellDetails[row][col].parent_j;
row = temp_row;
col = temp_col;
}
path.push({row, col}); // Thêm nút Start
while (!path.empty()) {
Pair p = path.top();
path.pop();
cout << "-> (" << p.first << ", " << p.second << ") ";
}
cout << endl;
}
// --- Hàm A* chính ---
// Kiểu dữ liệu cho Hàng đợi ưu tiên (OPEN list)
// pair<f_score, pair<x, y>>
typedef pair<double, Pair> pPair;
void aStarSearch(const vector<vector<int>>& grid, Pair src, Pair dest) {
// 1. Kiểm tra tính hợp lệ của Start và Goal
if (!isValid(src.first, src.second) || !isValid(dest.first, dest.second)) {
cout << "Start hoặc Goal không hợp lệ" << endl;
return;
}
if (!isUnBlocked(grid, src.first, src.second) || !isUnBlocked(grid, dest.first, dest.second)) {
cout << "Start hoặc Goal bị chặn" << endl;
return;
}
if (isDestination(src.first, src.second, dest)) {
cout << "Chúng ta đã ở đích" << endl;
return;
}
// 2. Khởi tạo
// Mảng 2D lưu chi tiết các ô
vector<vector<cell>> cellDetails(ROW, vector<cell>(COL));
// Mảng 2D cho CLOSE list (đã xử lý hay chưa)
vector<vector<bool>> closedList(ROW, vector<bool>(COL, false));
// Khởi tạo tất cả các ô
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
cellDetails[i][j].f = FLT_MAX;
cellDetails[i][j].g = FLT_MAX;
cellDetails[i][j].h = FLT_MAX;
cellDetails[i][j].parent_i = -1;
cellDetails[i][j].parent_j = -1;
}
}
// Khởi tạo ô bắt đầu
int i = src.first, j = src.second;
cellDetails[i][j].g = 0.0;
cellDetails[i][j].h = calculateHValue(i, j, dest);
cellDetails[i][j].f = cellDetails[i][j].g + cellDetails[i][j].h;
cellDetails[i][j].parent_i = i;
cellDetails[i][j].parent_j = j;
// Hàng đợi ưu tiên (OPEN list)
// Sắp xếp theo f tăng dần (min-heap)
priority_queue<pPair, vector<pPair>, greater<pPair>> openList;
// Thêm nút bắt đầu vào OPEN list
openList.push({cellDetails[i][j].f, {i, j}});
bool foundDest = false;
// 3. Vòng lặp chính
while (!openList.empty()) {
// Lấy nút có f thấp nhất
pPair p = openList.top();
openList.pop();
// Lấy tọa độ
i = p.second.first;
j = p.second.second;
// Đánh dấu là đã xử lý
closedList[i][j] = true;
// 4. Duyệt các hàng xóm (4 hướng)
// (Thay đổi để duyệt 8 hướng nếu cần)
int dRow[] = {-1, 1, 0, 0};
int dCol[] = {0, 0, 1, -1};
for (int k = 0; k < 4; ++k) {
int n_i = i + dRow[k];
int n_j = j + dCol[k];
// Nếu hàng xóm hợp lệ
if (isValid(n_i, n_j)) {
// 5. Kiểm tra Đích
if (isDestination(n_i, n_j, dest)) {
// Đặt cha cho ô đích và truy vết
cellDetails[n_i][n_j].parent_i = i;
cellDetails[n_i][n_j].parent_j = j;
cout << "Tìm thấy đường đi!" << endl;
tracePath(cellDetails, dest);
foundDest = true;
return; // Thoát hàm
}
// Nếu hàng xóm chưa xử lý VÀ không bị chặn
else if (!closedList[n_i][n_j] && isUnBlocked(grid, n_i, n_j)) {
// 6. Tính toán chi phí
double gNew = cellDetails[i][j].g + 1.0; // Chi phí mỗi bước là 1
double hNew = calculateHValue(n_i, n_j, dest);
double fNew = gNew + hNew;
// 7. Cập nhật đường đi tốt hơn
if (cellDetails[n_i][n_j].f == FLT_MAX || cellDetails[n_i][n_j].f > fNew) {
// Thêm vào OPEN list
openList.push({fNew, {n_i, n_j}});
// Cập nhật chi tiết
cellDetails[n_i][n_j].f = fNew;
cellDetails[n_i][n_j].g = gNew;
cellDetails[n_i][n_j].h = hNew;
cellDetails[n_i][n_j].parent_i = i;
cellDetails[n_i][n_j].parent_j = j;
}
}
}
}
}
// 8. Không tìm thấy đường
if (!foundDest) {
cout << "Không tìm thấy đường đi" << endl;
}
}
// --- Hàm main để chạy ---
int main() {
/* Bản đồ
0 = Đường đi
1 = Tường (Obstacle)
*/
vector<vector<int>> grid = {
{0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 1, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
// Điểm bắt đầu (Start)
Pair src = {8, 0};
// Điểm kết thúc (Goal)
Pair dest = {0, 9};
aStarSearch(grid, src, dest);
return 0;
}
I2luY2x1ZGU8Yml0cy9zdGRjKysuaD4KdXNpbmcgbmFtZXNwYWNlIHN0ZDsKCi8vIMSQ4buLbmggbmdoxKlhIGvDrWNoIHRoxrDhu5tjIGzGsOG7m2kKI2RlZmluZSBST1cgOQojZGVmaW5lIENPTCAxMAoKLy8gQ+G6pXUgdHLDumMgdOG7jWEgxJHhu5kgKFBvaW50KQp0eXBlZGVmIHBhaXI8aW50LCBpbnQ+IFBhaXI7CgovLyBD4bqldSB0csO6YyBsxrB1IHRy4buvIHRow7RuZyB0aW4gY+G7p2EgbeG7mXQgw7QgKE5vZGUpCnN0cnVjdCBjZWxsIHsKICAgIC8vIFThu41hIMSR4buZIGNoYSAocGFyZW50KSDEkeG7gyB0cnV5IHbhur90CiAgICAvLyBwYWlyPGludCwgaW50PiBwYXJlbnQ7CiAgICBpbnQgcGFyZW50X2ksIHBhcmVudF9qOwogICAgLy8gZiA9IGcgKyBoCiAgICBkb3VibGUgZiwgZywgaDsKfTsKCi8vIC0tLSBDw6FjIGjDoG0gaOG7lyB0cuG7oyAtLS0KCi8vIEtp4buDbSB0cmEgeGVtIMO0IChyb3csIGNvbCkgY8OzIG7hurFtIHRyb25nIGzGsOG7m2kga2jDtG5nCmJvb2wgaXNWYWxpZChpbnQgcm93LCBpbnQgY29sKSB7CiAgICByZXR1cm4gKHJvdyA+PSAwKSAmJiAocm93IDwgUk9XKSAmJiAoY29sID49IDApICYmIChjb2wgPCBDT0wpOwp9CgovLyBLaeG7g20gdHJhIHhlbSDDtCBjw7MgcGjhuqNpIGzDoCB0xrDhu51uZyAoYuG7iyBjaOG6t24pIGtow7RuZwpib29sIGlzVW5CbG9ja2VkKGNvbnN0IHZlY3Rvcjx2ZWN0b3I8aW50Pj4mIGdyaWQsIGludCByb3csIGludCBjb2wpIHsKICAgIC8vIDEgPSBi4buLIGNo4bq3biwgMCA9IGtow7RuZyBi4buLIGNo4bq3bgogICAgcmV0dXJuIGdyaWRbcm93XVtjb2xdID09IDA7Cn0KCi8vIEtp4buDbSB0cmEgeGVtIGPDsyBwaOG6o2kgbMOgIMO0IMSRw61jaCBraMO0bmcKYm9vbCBpc0Rlc3RpbmF0aW9uKGludCByb3csIGludCBjb2wsIFBhaXIgZGVzdCkgewogICAgcmV0dXJuIChyb3cgPT0gZGVzdC5maXJzdCAmJiBjb2wgPT0gZGVzdC5zZWNvbmQpOwp9CgovLyBUw61uaCBIZXVyaXN0aWMgKGgpIC0gZMO5bmcga2hv4bqjbmcgY8OhY2ggTWFuaGF0dGFuCmRvdWJsZSBjYWxjdWxhdGVIVmFsdWUoaW50IHJvdywgaW50IGNvbCwgUGFpciBkZXN0KSB7CiAgICAvLyByZXR1cm4gc3FydChwb3cocm93IC0gZGVzdC5maXJzdCwgMikgKyBwb3coY29sIC0gZGVzdC5zZWNvbmQsIDIpKTsgLy8gRXVjbGlkZWFuCiAgICByZXR1cm4gYWJzKHJvdyAtIGRlc3QuZmlyc3QpICsgYWJzKGNvbCAtIGRlc3Quc2Vjb25kKTsgLy8gTWFuaGF0dGFuCn0KCi8vIFRydXkgduG6v3QgxJHGsOG7nW5nIMSRaSB04burIMSRw61jaCB24buBCnZvaWQgdHJhY2VQYXRoKGNvbnN0IHZlY3Rvcjx2ZWN0b3I8Y2VsbD4+JiBjZWxsRGV0YWlscywgUGFpciBkZXN0KSB7CiAgICBjb3V0IDw8ICLEkMaw4budbmcgxJFpIGzDoDogIiA8PCBlbmRsOwogICAgc3RhY2s8UGFpcj4gcGF0aDsKCiAgICBpbnQgcm93ID0gZGVzdC5maXJzdDsKICAgIGludCBjb2wgPSBkZXN0LnNlY29uZDsKCiAgICB3aGlsZSAoIShjZWxsRGV0YWlsc1tyb3ddW2NvbF0ucGFyZW50X2kgPT0gcm93ICYmIGNlbGxEZXRhaWxzW3Jvd11bY29sXS5wYXJlbnRfaiA9PSBjb2wpKSB7CiAgICAgICAgcGF0aC5wdXNoKHtyb3csIGNvbH0pOwogICAgICAgIGludCB0ZW1wX3JvdyA9IGNlbGxEZXRhaWxzW3Jvd11bY29sXS5wYXJlbnRfaTsKICAgICAgICBpbnQgdGVtcF9jb2wgPSBjZWxsRGV0YWlsc1tyb3ddW2NvbF0ucGFyZW50X2o7CiAgICAgICAgcm93ID0gdGVtcF9yb3c7CiAgICAgICAgY29sID0gdGVtcF9jb2w7CiAgICB9CgogICAgcGF0aC5wdXNoKHtyb3csIGNvbH0pOyAvLyBUaMOqbSBuw7p0IFN0YXJ0CgogICAgd2hpbGUgKCFwYXRoLmVtcHR5KCkpIHsKICAgICAgICBQYWlyIHAgPSBwYXRoLnRvcCgpOwogICAgICAgIHBhdGgucG9wKCk7CiAgICAgICAgY291dCA8PCAiLT4gKCIgPDwgcC5maXJzdCA8PCAiLCAiIDw8IHAuc2Vjb25kIDw8ICIpICI7CiAgICB9CiAgICBjb3V0IDw8IGVuZGw7Cn0KCi8vIC0tLSBIw6BtIEEqIGNow61uaCAtLS0KCi8vIEtp4buDdSBk4buvIGxp4buHdSBjaG8gSMOgbmcgxJHhu6NpIMawdSB0acOqbiAoT1BFTiBsaXN0KQovLyBwYWlyPGZfc2NvcmUsIHBhaXI8eCwgeT4+CnR5cGVkZWYgcGFpcjxkb3VibGUsIFBhaXI+IHBQYWlyOwoKdm9pZCBhU3RhclNlYXJjaChjb25zdCB2ZWN0b3I8dmVjdG9yPGludD4+JiBncmlkLCBQYWlyIHNyYywgUGFpciBkZXN0KSB7CiAgICAKICAgIC8vIDEuIEtp4buDbSB0cmEgdMOtbmggaOG7o3AgbOG7hyBj4bunYSBTdGFydCB2w6AgR29hbAogICAgaWYgKCFpc1ZhbGlkKHNyYy5maXJzdCwgc3JjLnNlY29uZCkgfHwgIWlzVmFsaWQoZGVzdC5maXJzdCwgZGVzdC5zZWNvbmQpKSB7CiAgICAgICAgY291dCA8PCAiU3RhcnQgaG/hurdjIEdvYWwga2jDtG5nIGjhu6NwIGzhu4ciIDw8IGVuZGw7CiAgICAgICAgcmV0dXJuOwogICAgfQoKICAgIGlmICghaXNVbkJsb2NrZWQoZ3JpZCwgc3JjLmZpcnN0LCBzcmMuc2Vjb25kKSB8fCAhaXNVbkJsb2NrZWQoZ3JpZCwgZGVzdC5maXJzdCwgZGVzdC5zZWNvbmQpKSB7CiAgICAgICAgY291dCA8PCAiU3RhcnQgaG/hurdjIEdvYWwgYuG7iyBjaOG6t24iIDw8IGVuZGw7CiAgICAgICAgcmV0dXJuOwogICAgfQoKICAgIGlmIChpc0Rlc3RpbmF0aW9uKHNyYy5maXJzdCwgc3JjLnNlY29uZCwgZGVzdCkpIHsKICAgICAgICBjb3V0IDw8ICJDaMO6bmcgdGEgxJHDoyDhu58gxJHDrWNoIiA8PCBlbmRsOwogICAgICAgIHJldHVybjsKICAgIH0KCiAgICAvLyAyLiBLaOG7n2kgdOG6oW8KICAgIC8vIE3huqNuZyAyRCBsxrB1IGNoaSB0aeG6v3QgY8OhYyDDtAogICAgdmVjdG9yPHZlY3RvcjxjZWxsPj4gY2VsbERldGFpbHMoUk9XLCB2ZWN0b3I8Y2VsbD4oQ09MKSk7CiAgICAKICAgIC8vIE3huqNuZyAyRCBjaG8gQ0xPU0UgbGlzdCAoxJHDoyB44butIGzDvSBoYXkgY2jGsGEpCiAgICB2ZWN0b3I8dmVjdG9yPGJvb2w+PiBjbG9zZWRMaXN0KFJPVywgdmVjdG9yPGJvb2w+KENPTCwgZmFsc2UpKTsKCiAgICAvLyBLaOG7n2kgdOG6oW8gdOG6pXQgY+G6oyBjw6FjIMO0CiAgICBmb3IgKGludCBpID0gMDsgaSA8IFJPVzsgKytpKSB7CiAgICAgICAgZm9yIChpbnQgaiA9IDA7IGogPCBDT0w7ICsraikgewogICAgICAgICAgICBjZWxsRGV0YWlsc1tpXVtqXS5mID0gRkxUX01BWDsKICAgICAgICAgICAgY2VsbERldGFpbHNbaV1bal0uZyA9IEZMVF9NQVg7CiAgICAgICAgICAgIGNlbGxEZXRhaWxzW2ldW2pdLmggPSBGTFRfTUFYOwogICAgICAgICAgICBjZWxsRGV0YWlsc1tpXVtqXS5wYXJlbnRfaSA9IC0xOwogICAgICAgICAgICBjZWxsRGV0YWlsc1tpXVtqXS5wYXJlbnRfaiA9IC0xOwogICAgICAgIH0KICAgIH0KCiAgICAvLyBLaOG7n2kgdOG6oW8gw7QgYuG6r3QgxJHhuqd1CiAgICBpbnQgaSA9IHNyYy5maXJzdCwgaiA9IHNyYy5zZWNvbmQ7CiAgICBjZWxsRGV0YWlsc1tpXVtqXS5nID0gMC4wOwogICAgY2VsbERldGFpbHNbaV1bal0uaCA9IGNhbGN1bGF0ZUhWYWx1ZShpLCBqLCBkZXN0KTsKICAgIGNlbGxEZXRhaWxzW2ldW2pdLmYgPSBjZWxsRGV0YWlsc1tpXVtqXS5nICsgY2VsbERldGFpbHNbaV1bal0uaDsKICAgIGNlbGxEZXRhaWxzW2ldW2pdLnBhcmVudF9pID0gaTsKICAgIGNlbGxEZXRhaWxzW2ldW2pdLnBhcmVudF9qID0gajsKCiAgICAvLyBIw6BuZyDEkeG7o2kgxrB1IHRpw6puIChPUEVOIGxpc3QpCiAgICAvLyBT4bqvcCB44bq/cCB0aGVvIGYgdMSDbmcgZOG6p24gKG1pbi1oZWFwKQogICAgcHJpb3JpdHlfcXVldWU8cFBhaXIsIHZlY3RvcjxwUGFpcj4sIGdyZWF0ZXI8cFBhaXI+PiBvcGVuTGlzdDsKCiAgICAvLyBUaMOqbSBuw7p0IGLhuq90IMSR4bqndSB2w6BvIE9QRU4gbGlzdAogICAgb3Blbkxpc3QucHVzaCh7Y2VsbERldGFpbHNbaV1bal0uZiwge2ksIGp9fSk7CgogICAgYm9vbCBmb3VuZERlc3QgPSBmYWxzZTsKCiAgICAvLyAzLiBWw7JuZyBs4bq3cCBjaMOtbmgKICAgIHdoaWxlICghb3Blbkxpc3QuZW1wdHkoKSkgewogICAgICAgIC8vIEzhuqV5IG7DunQgY8OzIGYgdGjhuqVwIG5o4bqldAogICAgICAgIHBQYWlyIHAgPSBvcGVuTGlzdC50b3AoKTsKICAgICAgICBvcGVuTGlzdC5wb3AoKTsKCiAgICAgICAgLy8gTOG6pXkgdOG7jWEgxJHhu5kKICAgICAgICBpID0gcC5zZWNvbmQuZmlyc3Q7CiAgICAgICAgaiA9IHAuc2Vjb25kLnNlY29uZDsKCiAgICAgICAgLy8gxJDDoW5oIGThuqV1IGzDoCDEkcOjIHjhu60gbMO9CiAgICAgICAgY2xvc2VkTGlzdFtpXVtqXSA9IHRydWU7CgogICAgICAgIC8vIDQuIER1eeG7h3QgY8OhYyBow6BuZyB4w7NtICg0IGjGsOG7m25nKQogICAgICAgIC8vIChUaGF5IMSR4buVaSDEkeG7gyBkdXnhu4d0IDggaMaw4bubbmcgbuG6v3UgY+G6p24pCiAgICAgICAgaW50IGRSb3dbXSA9IHstMSwgMSwgMCwgMH07CiAgICAgICAgaW50IGRDb2xbXSA9IHswLCAwLCAxLCAtMX07CgogICAgICAgIGZvciAoaW50IGsgPSAwOyBrIDwgNDsgKytrKSB7CiAgICAgICAgICAgIGludCBuX2kgPSBpICsgZFJvd1trXTsKICAgICAgICAgICAgaW50IG5faiA9IGogKyBkQ29sW2tdOwoKICAgICAgICAgICAgLy8gTuG6v3UgaMOgbmcgeMOzbSBo4bujcCBs4buHCiAgICAgICAgICAgIGlmIChpc1ZhbGlkKG5faSwgbl9qKSkgewogICAgICAgICAgICAgICAgLy8gNS4gS2nhu4NtIHRyYSDEkMOtY2gKICAgICAgICAgICAgICAgIGlmIChpc0Rlc3RpbmF0aW9uKG5faSwgbl9qLCBkZXN0KSkgewogICAgICAgICAgICAgICAgICAgIC8vIMSQ4bq3dCBjaGEgY2hvIMO0IMSRw61jaCB2w6AgdHJ1eSB24bq/dAogICAgICAgICAgICAgICAgICAgIGNlbGxEZXRhaWxzW25faV1bbl9qXS5wYXJlbnRfaSA9IGk7CiAgICAgICAgICAgICAgICAgICAgY2VsbERldGFpbHNbbl9pXVtuX2pdLnBhcmVudF9qID0gajsKICAgICAgICAgICAgICAgICAgICBjb3V0IDw8ICJUw6xtIHRo4bqleSDEkcaw4budbmcgxJFpISIgPDwgZW5kbDsKICAgICAgICAgICAgICAgICAgICB0cmFjZVBhdGgoY2VsbERldGFpbHMsIGRlc3QpOwogICAgICAgICAgICAgICAgICAgIGZvdW5kRGVzdCA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuOyAvLyBUaG/DoXQgaMOgbQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAvLyBO4bq/dSBow6BuZyB4w7NtIGNoxrBhIHjhu60gbMO9IFbDgCBraMO0bmcgYuG7iyBjaOG6t24KICAgICAgICAgICAgICAgIGVsc2UgaWYgKCFjbG9zZWRMaXN0W25faV1bbl9qXSAmJiBpc1VuQmxvY2tlZChncmlkLCBuX2ksIG5faikpIHsKICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAvLyA2LiBUw61uaCB0b8OhbiBjaGkgcGjDrQogICAgICAgICAgICAgICAgICAgIGRvdWJsZSBnTmV3ID0gY2VsbERldGFpbHNbaV1bal0uZyArIDEuMDsgLy8gQ2hpIHBow60gbeG7l2kgYsaw4bubYyBsw6AgMQogICAgICAgICAgICAgICAgICAgIGRvdWJsZSBoTmV3ID0gY2FsY3VsYXRlSFZhbHVlKG5faSwgbl9qLCBkZXN0KTsKICAgICAgICAgICAgICAgICAgICBkb3VibGUgZk5ldyA9IGdOZXcgKyBoTmV3OwoKICAgICAgICAgICAgICAgICAgICAvLyA3LiBD4bqtcCBuaOG6rXQgxJHGsOG7nW5nIMSRaSB04buRdCBoxqFuCiAgICAgICAgICAgICAgICAgICAgaWYgKGNlbGxEZXRhaWxzW25faV1bbl9qXS5mID09IEZMVF9NQVggfHwgY2VsbERldGFpbHNbbl9pXVtuX2pdLmYgPiBmTmV3KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAvLyBUaMOqbSB2w6BvIE9QRU4gbGlzdAogICAgICAgICAgICAgICAgICAgICAgICBvcGVuTGlzdC5wdXNoKHtmTmV3LCB7bl9pLCBuX2p9fSk7CgogICAgICAgICAgICAgICAgICAgICAgICAvLyBD4bqtcCBuaOG6rXQgY2hpIHRp4bq/dAogICAgICAgICAgICAgICAgICAgICAgICBjZWxsRGV0YWlsc1tuX2ldW25fal0uZiA9IGZOZXc7CiAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxEZXRhaWxzW25faV1bbl9qXS5nID0gZ05ldzsKICAgICAgICAgICAgICAgICAgICAgICAgY2VsbERldGFpbHNbbl9pXVtuX2pdLmggPSBoTmV3OwogICAgICAgICAgICAgICAgICAgICAgICBjZWxsRGV0YWlsc1tuX2ldW25fal0ucGFyZW50X2kgPSBpOwogICAgICAgICAgICAgICAgICAgICAgICBjZWxsRGV0YWlsc1tuX2ldW25fal0ucGFyZW50X2ogPSBqOwogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0KCiAgICAvLyA4LiBLaMO0bmcgdMOsbSB0aOG6pXkgxJHGsOG7nW5nCiAgICBpZiAoIWZvdW5kRGVzdCkgewogICAgICAgIGNvdXQgPDwgIktow7RuZyB0w6xtIHRo4bqleSDEkcaw4budbmcgxJFpIiA8PCBlbmRsOwogICAgfQp9CgovLyAtLS0gSMOgbSBtYWluIMSR4buDIGNo4bqheSAtLS0KaW50IG1haW4oKSB7CiAgICAvKiBC4bqjbiDEkeG7kwogICAgMCA9IMSQxrDhu51uZyDEkWkKICAgIDEgPSBUxrDhu51uZyAoT2JzdGFjbGUpCiAgICAqLwogICAgdmVjdG9yPHZlY3RvcjxpbnQ+PiBncmlkID0gewogICAgICAgIHswLCAwLCAxLCAwLCAwLCAwLCAwLCAxLCAwLCAwfSwKICAgICAgICB7MCwgMCwgMCwgMCwgMCwgMCwgMCwgMSwgMCwgMH0sCiAgICAgICAgezAsIDAsIDAsIDAsIDEsIDAsIDAsIDAsIDAsIDB9LAogICAgICAgIHswLCAwLCAwLCAwLCAxLCAwLCAwLCAxLCAwLCAwfSwKICAgICAgICB7MCwgMCwgMSwgMSwgMSwgMCwgMCwgMSwgMCwgMH0sCiAgICAgICAgezAsIDAsIDAsIDAsIDEsIDAsIDAsIDEsIDAsIDB9LAogICAgICAgIHswLCAwLCAwLCAwLCAxLCAwLCAwLCAxLCAwLCAwfSwKICAgICAgICB7MCwgMCwgMCwgMCwgMSwgMCwgMCwgMSwgMCwgMH0sCiAgICAgICAgezAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDB9CiAgICB9OwoKICAgIC8vIMSQaeG7g20gYuG6r3QgxJHhuqd1IChTdGFydCkKICAgIFBhaXIgc3JjID0gezgsIDB9OwoKICAgIC8vIMSQaeG7g20ga+G6v3QgdGjDumMgKEdvYWwpCiAgICBQYWlyIGRlc3QgPSB7MCwgOX07CgogICAgYVN0YXJTZWFyY2goZ3JpZCwgc3JjLCBkZXN0KTsKCiAgICByZXR1cm4gMDsKfQo=