#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <limits>
#include <iomanip>
using namespace std;
struct Point {
double x, y;
} ;
struct AStarNode {
int id;
double f, g;
bool operator> ( const AStarNode& other) const {
return f > other.f ;
}
} ;
unordered_map< string, int > node_ids;
unordered_map< int , string> node_names;
int current_id = 0 ;
int getId( const string& name) {
if ( node_ids.find ( name) == node_ids.end ( ) ) {
node_ids[ name] = current_id;
node_names[ current_id] = name;
current_id++ ;
}
return node_ids[ name] ;
}
double calculate_heuristic( int node_id, const vector< Point> & coordinates, const unordered_set< int > & exits) {
double min_dist = numeric_limits< double > :: infinity ( ) ;
for ( int exit_id : exits) {
double dx = coordinates[ node_id] .x - coordinates[ exit_id] .x ;
double dy = coordinates[ node_id] .y - coordinates[ exit_id] .y ;
double feet_dist = sqrt ( dx * dx + dy * dy) * 0.0729 ;
if ( feet_dist < min_dist) min_dist = feet_dist;
}
return min_dist;
}
vector< int > a_star_evacuation(
int start_node,
const unordered_set< int > & exits,
const vector< vector< pair< int , double >>> & graph,
const vector< Point> & coordinates,
const unordered_set< int > & fire_nodes,
double & out_distance)
{
int num_nodes = graph.size ( ) ;
vector< double > g_score( num_nodes, numeric_limits< double > :: infinity ( ) ) ;
g_score[ start_node] = 0.0 ;
vector< int > parent( num_nodes, - 1 ) ;
priority_queue< AStarNode, vector< AStarNode> , greater< AStarNode>> open_set;
open_set.push ( { start_node, calculate_heuristic( start_node, coordinates, exits) , 0.0 } ) ;
while ( ! open_set.empty ( ) ) {
AStarNode current = open_set.top ( ) ;
open_set.pop ( ) ;
int u = current.id ;
if ( exits.count ( u) ) {
out_distance = current.g ;
vector< int > path;
int curr = u;
while ( curr ! = - 1 ) {
path.push_back ( curr) ;
curr = parent[ curr] ;
}
reverse( path.begin ( ) , path.end ( ) ) ;
return path;
}
if ( current.g > g_score[ u] ) continue ;
for ( const auto & edge : graph[ u] ) {
int v = edge.first ;
double weight = edge.second ;
if ( fire_nodes.count ( v) ) continue ;
double tentative_g = g_score[ u] + weight;
if ( tentative_g < g_score[ v] ) {
parent[ v] = u;
g_score[ v] = tentative_g;
double f = tentative_g + calculate_heuristic( v, coordinates, exits) ;
open_set.push ( { v, f, tentative_g} ) ;
}
}
}
return { } ;
}
int main( ) {
// 1. Initialize Coordinates
vector< Point> coords_temp( 38 ) ;
auto addNode = [ & ] ( string name, double x, double y) {
int id = getId( name) ;
coords_temp[ id] = { x, y} ;
} ;
addNode( "door[0]" , 638.0 , 213.0 ) ; addNode( "door[1]" , 680.0 , 1049.0 ) ;
addNode( "door[2]" , 1103.0 , 1420.0 ) ; addNode( "door[3]" , 1640.0 , 1626.0 ) ;
addNode( "door[4]" , 1635.0 , 1167.0 ) ; addNode( "door[5]" , 2358.0 , 1294.0 ) ;
addNode( "door[6]" , 2718.0 , 1578.0 ) ; addNode( "door[7]" , 2651.0 , 1367.0 ) ;
addNode( "door[8]" , 3132.0 , 1179.0 ) ; addNode( "door[9]" , 3525.0 , 1049.0 ) ;
addNode( "door[10]" , 3522.0 , 1505.0 ) ;
addNode( "exits[0]" , 638.0 , 1240.0 ) ; addNode( "exits[1]" , 1095.0 , 1917.0 ) ;
addNode( "exits[2]" , 2317.0 , 1767.0 ) ; addNode( "exits[3]" , 3038.0 , 959.0 ) ;
addNode( "coridor[0]" , 736.0 , 138.0 ) ; addNode( "coridor[1]" , 1220.0 , 818.0 ) ;
addNode( "coridor[2]" , 718.0 , 1182.0 ) ; addNode( "coridor[3]" , 884.0 , 1417.0 ) ;
addNode( "coridor[4]" , 1032.0 , 1309.0 ) ; addNode( "coridor[5]" , 1385.0 , 1049.0 ) ;
addNode( "coridor[6]" , 1186.0 , 1845.0 ) ; addNode( "coridor[7]" , 1473.0 , 1169.0 ) ;
addNode( "coridor[8]" , 1470.0 , 1045.0 ) ; addNode( "coridor[9]" , 1742.0 , 1545.0 ) ;
addNode( "coridor[10]" , 1934.0 , 1667.0 ) ; addNode( "coridor[11]" , 2320.0 , 1665.0 ) ;
addNode( "coridor[12]" , 2433.0 , 1665.0 ) ; addNode( "coridor[13]" , 2434.0 , 1046.0 ) ;
addNode( "coridor[14]" , 2434.0 , 1293.0 ) ; addNode( "coridor[15]" , 2559.0 , 1049.0 ) ;
addNode( "coridor[16]" , 2558.0 , 1368.0 ) ; addNode( "coridor[17]" , 2559.0 , 1668.0 ) ;
addNode( "coridor[18]" , 2721.0 , 1665.0 ) ; addNode( "coridor[19]" , 3523.0 , 1669.0 ) ;
addNode( "coridor[20]" , 3038.0 , 1049.0 ) ; addNode( "coridor[21]" , 3133.0 , 1048.0 ) ;
addNode( "coridor[22]" , 3398.0 , 1048.0 ) ;
// 2. Build Graph
vector< vector< pair< int , double >>> graph( 38 ) ;
auto addEdge = [ & ] ( string u, string v, double w) {
graph[ getId( u) ] .push_back ( { getId( v) , w} ) ;
graph[ getId( v) ] .push_back ( { getId( u) , w} ) ;
} ;
addEdge( "door[0]" , "coridor[0]" , 9.1 ) ; addEdge( "door[0]" , "door[1]" , 61.02 ) ;
addEdge( "door[1]" , "coridor[2]" , 9.95 ) ; addEdge( "door[2]" , "coridor[4]" , 9.33 ) ;
addEdge( "door[2]" , "door[3]" , 41.53 ) ; addEdge( "door[3]" , "coridor[9]" , 9.52 ) ;
addEdge( "door[4]" , "coridor[7]" , 12.02 ) ; addEdge( "door[4]" , "door[5]" , 52.80 ) ;
addEdge( "door[5]" , "coridor[14]" , 5.74 ) ; addEdge( "door[6]" , "door[7]" , 15.77 ) ;
addEdge( "door[6]" , "coridor[18]" , 6.48 ) ; addEdge( "door[7]" , "door[8]" , 37.92 ) ;
addEdge( "door[7]" , "coridor[16]" , 6.87 ) ; addEdge( "door[8]" , "coridor[21]" , 9.27 ) ;
addEdge( "door[9]" , "door[10]" , 33.09 ) ; addEdge( "door[9]" , "coridor[22]" , 9.13 ) ;
addEdge( "door[10]" , "coridor[19]" , 12.06 ) ;
addEdge( "exits[0]" , "coridor[2]" , 6.84 ) ; addEdge( "exits[1]" , "coridor[6]" , 8.39 ) ;
addEdge( "exits[2]" , "coridor[11]" , 6.53 ) ; addEdge( "exits[3]" , "coridor[20]" , 6.49 ) ;
addEdge( "coridor[0]" , "coridor[1]" , 60.81 ) ; addEdge( "coridor[1]" , "coridor[2]" , 45.29 ) ;
addEdge( "coridor[1]" , "coridor[5]" , 20.31 ) ; addEdge( "coridor[2]" , "coridor[3]" , 20.98 ) ;
addEdge( "coridor[3]" , "coridor[4]" , 13.36 ) ; addEdge( "coridor[3]" , "coridor[6]" , 38.41 ) ;
addEdge( "coridor[4]" , "coridor[5]" , 31.51 ) ; addEdge( "coridor[5]" , "coridor[7]" , 10.75 ) ;
addEdge( "coridor[5]" , "coridor[8]" , 6.08 ) ; addEdge( "coridor[7]" , "coridor[9]" , 34.22 ) ;
addEdge( "coridor[8]" , "coridor[13]" , 70.45 ) ; addEdge( "coridor[9]" , "coridor[10]" , 16.29 ) ;
addEdge( "coridor[10]" , "coridor[11]" , 28.24 ) ; addEdge( "coridor[11]" , "coridor[12]" , 8.52 ) ;
addEdge( "coridor[12]" , "coridor[14]" , 26.64 ) ; addEdge( "coridor[12]" , "coridor[17]" , 8.92 ) ;
addEdge( "coridor[13]" , "coridor[14]" , 17.76 ) ; addEdge( "coridor[13]" , "coridor[15]" , 8.88 ) ;
addEdge( "coridor[15]" , "coridor[16]" , 23.21 ) ; addEdge( "coridor[15]" , "coridor[20]" , 34.74 ) ;
addEdge( "coridor[16]" , "coridor[17]" , 21.24 ) ; addEdge( "coridor[17]" , "coridor[18]" , 11.64 ) ;
addEdge( "coridor[18]" , "coridor[19]" , 59.08 ) ; addEdge( "coridor[20]" , "coridor[21]" , 7.02 ) ;
addEdge( "coridor[21]" , "coridor[22]" , 19.48 ) ;
unordered_set< int > exit_ids = {
getId( "exits[0]" ) , getId( "exits[1]" ) ,
getId( "exits[2]" ) , getId( "exits[3]" )
} ;
vector< string> all_doors = {
"door[0]" , "door[1]" , "door[2]" , "door[3]" , "door[4]" ,
"door[5]" , "door[6]" , "door[7]" , "door[8]" , "door[9]" , "door[10]"
} ;
cout << "--- EVACUATION SYSTEM CONTROL ---\n " ;
cout << "Enter the number of active fire zones: " ;
int num_fires;
cin >> num_fires;
unordered_set< int > active_fires;
for ( int i = 0 ; i < num_fires; ++ i) {
string fire_location;
cout << "Enter location for Fire " << ( i + 1 ) << " (e.g., coridor[15], door[1]): " ;
cin >> fire_location;
if ( node_ids.find ( fire_location) ! = node_ids.end ( ) ) {
active_fires.insert ( getId( fire_location) ) ;
} else {
cout << "Warning: Node '" << fire_location << "' does not exist. Skipping.\n " ;
}
}
cout << "\n Calculating safest routes avoiding all fire zones...\n " ;
cout << string( 60 , '=' ) << "\n \n " ;
for ( const string& door_name : all_doors) {
cout << "From " << door_name << ":\n " ;
if ( active_fires.count ( getId( door_name) ) ) {
cout << " [!] THIS DOOR IS ON FIRE. EVACUATION IMPOSSIBLE.\n \n " ;
continue ;
}
double total_distance = 0.0 ;
vector< int > path = a_star_evacuation( getId( door_name) , exit_ids, graph, coords_temp, active_fires, total_distance) ;
if ( ! path.empty ( ) ) {
cout << " Path: " ;
for ( int i = 0 ; i < path.size ( ) ; ++ i) {
cout << node_names[ path[ i] ] << ( i == path.size ( ) - 1 ? "" : " -> " ) ;
}
double clear_time_sec = total_distance / 4.0 ;
double smoke_time_sec = total_distance / 1.5 ;
cout << fixed << setprecision( 2 ) ;
cout << "\n Distance: " << total_distance << " ft" ;
cout << "\n Est. Time (Clear): " << clear_time_sec << " seconds" ;
cout << "\n Est. Time (Smoky): " << smoke_time_sec << " seconds\n \n " ;
} else {
cout << " [!] NO SAFE ROUTE. FIRES BLOCK ALL PATHS TO EXITS.\n \n " ;
}
}
return 0 ;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8cXVldWU+CiNpbmNsdWRlIDxjbWF0aD4KI2luY2x1ZGUgPHVub3JkZXJlZF9zZXQ+CiNpbmNsdWRlIDx1bm9yZGVyZWRfbWFwPgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8bGltaXRzPgojaW5jbHVkZSA8aW9tYW5pcD4gCgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKc3RydWN0IFBvaW50IHsKICAgIGRvdWJsZSB4LCB5Owp9OwoKc3RydWN0IEFTdGFyTm9kZSB7CiAgICBpbnQgaWQ7CiAgICBkb3VibGUgZiwgZzsKICAgIGJvb2wgb3BlcmF0b3I+KGNvbnN0IEFTdGFyTm9kZSYgb3RoZXIpIGNvbnN0IHsKICAgICAgICByZXR1cm4gZiA+IG90aGVyLmY7CiAgICB9Cn07Cgp1bm9yZGVyZWRfbWFwPHN0cmluZywgaW50PiBub2RlX2lkczsKdW5vcmRlcmVkX21hcDxpbnQsIHN0cmluZz4gbm9kZV9uYW1lczsKaW50IGN1cnJlbnRfaWQgPSAwOwoKaW50IGdldElkKGNvbnN0IHN0cmluZyYgbmFtZSkgewogICAgaWYgKG5vZGVfaWRzLmZpbmQobmFtZSkgPT0gbm9kZV9pZHMuZW5kKCkpIHsKICAgICAgICBub2RlX2lkc1tuYW1lXSA9IGN1cnJlbnRfaWQ7CiAgICAgICAgbm9kZV9uYW1lc1tjdXJyZW50X2lkXSA9IG5hbWU7CiAgICAgICAgY3VycmVudF9pZCsrOwogICAgfQogICAgcmV0dXJuIG5vZGVfaWRzW25hbWVdOwp9Cgpkb3VibGUgY2FsY3VsYXRlX2hldXJpc3RpYyhpbnQgbm9kZV9pZCwgY29uc3QgdmVjdG9yPFBvaW50PiYgY29vcmRpbmF0ZXMsIGNvbnN0IHVub3JkZXJlZF9zZXQ8aW50PiYgZXhpdHMpIHsKICAgIGRvdWJsZSBtaW5fZGlzdCA9IG51bWVyaWNfbGltaXRzPGRvdWJsZT46OmluZmluaXR5KCk7CiAgICBmb3IgKGludCBleGl0X2lkIDogZXhpdHMpIHsKICAgICAgICBkb3VibGUgZHggPSBjb29yZGluYXRlc1tub2RlX2lkXS54IC0gY29vcmRpbmF0ZXNbZXhpdF9pZF0ueDsKICAgICAgICBkb3VibGUgZHkgPSBjb29yZGluYXRlc1tub2RlX2lkXS55IC0gY29vcmRpbmF0ZXNbZXhpdF9pZF0ueTsKICAgICAgICBkb3VibGUgZmVldF9kaXN0ID0gc3FydChkeCAqIGR4ICsgZHkgKiBkeSkgKiAwLjA3Mjk7IAogICAgICAgIGlmIChmZWV0X2Rpc3QgPCBtaW5fZGlzdCkgbWluX2Rpc3QgPSBmZWV0X2Rpc3Q7CiAgICB9CiAgICByZXR1cm4gbWluX2Rpc3Q7Cn0KCgp2ZWN0b3I8aW50PiBhX3N0YXJfZXZhY3VhdGlvbigKICAgIGludCBzdGFydF9ub2RlLCAKICAgIGNvbnN0IHVub3JkZXJlZF9zZXQ8aW50PiYgZXhpdHMsIAogICAgY29uc3QgdmVjdG9yPHZlY3RvcjxwYWlyPGludCwgZG91YmxlPj4+JiBncmFwaCwgCiAgICBjb25zdCB2ZWN0b3I8UG9pbnQ+JiBjb29yZGluYXRlcywKICAgIGNvbnN0IHVub3JkZXJlZF9zZXQ8aW50PiYgZmlyZV9ub2RlcywgCiAgICBkb3VibGUmIG91dF9kaXN0YW5jZSkgCnsKICAgIGludCBudW1fbm9kZXMgPSBncmFwaC5zaXplKCk7CiAgICB2ZWN0b3I8ZG91YmxlPiBnX3Njb3JlKG51bV9ub2RlcywgbnVtZXJpY19saW1pdHM8ZG91YmxlPjo6aW5maW5pdHkoKSk7CiAgICBnX3Njb3JlW3N0YXJ0X25vZGVdID0gMC4wOwogICAgdmVjdG9yPGludD4gcGFyZW50KG51bV9ub2RlcywgLTEpOwoKICAgIHByaW9yaXR5X3F1ZXVlPEFTdGFyTm9kZSwgdmVjdG9yPEFTdGFyTm9kZT4sIGdyZWF0ZXI8QVN0YXJOb2RlPj4gb3Blbl9zZXQ7CiAgICBvcGVuX3NldC5wdXNoKHtzdGFydF9ub2RlLCBjYWxjdWxhdGVfaGV1cmlzdGljKHN0YXJ0X25vZGUsIGNvb3JkaW5hdGVzLCBleGl0cyksIDAuMH0pOwoKICAgIHdoaWxlICghb3Blbl9zZXQuZW1wdHkoKSkgewogICAgICAgIEFTdGFyTm9kZSBjdXJyZW50ID0gb3Blbl9zZXQudG9wKCk7CiAgICAgICAgb3Blbl9zZXQucG9wKCk7CiAgICAgICAgaW50IHUgPSBjdXJyZW50LmlkOwoKICAgICAgICBpZiAoZXhpdHMuY291bnQodSkpIHsKICAgICAgICAgICAgb3V0X2Rpc3RhbmNlID0gY3VycmVudC5nOyAKICAgICAgICAgICAgdmVjdG9yPGludD4gcGF0aDsKICAgICAgICAgICAgaW50IGN1cnIgPSB1OwogICAgICAgICAgICB3aGlsZSAoY3VyciAhPSAtMSkgewogICAgICAgICAgICAgICAgcGF0aC5wdXNoX2JhY2soY3Vycik7CiAgICAgICAgICAgICAgICBjdXJyID0gcGFyZW50W2N1cnJdOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldmVyc2UocGF0aC5iZWdpbigpLCBwYXRoLmVuZCgpKTsKICAgICAgICAgICAgcmV0dXJuIHBhdGg7CiAgICAgICAgfQoKICAgICAgICBpZiAoY3VycmVudC5nID4gZ19zY29yZVt1XSkgY29udGludWU7CgogICAgICAgIGZvciAoY29uc3QgYXV0byYgZWRnZSA6IGdyYXBoW3VdKSB7CiAgICAgICAgICAgIGludCB2ID0gZWRnZS5maXJzdDsKICAgICAgICAgICAgZG91YmxlIHdlaWdodCA9IGVkZ2Uuc2Vjb25kOwoKICAgICAgICAgICAgaWYgKGZpcmVfbm9kZXMuY291bnQodikpIGNvbnRpbnVlOyAKCiAgICAgICAgICAgIGRvdWJsZSB0ZW50YXRpdmVfZyA9IGdfc2NvcmVbdV0gKyB3ZWlnaHQ7CgogICAgICAgICAgICBpZiAodGVudGF0aXZlX2cgPCBnX3Njb3JlW3ZdKSB7CiAgICAgICAgICAgICAgICBwYXJlbnRbdl0gPSB1OwogICAgICAgICAgICAgICAgZ19zY29yZVt2XSA9IHRlbnRhdGl2ZV9nOwogICAgICAgICAgICAgICAgZG91YmxlIGYgPSB0ZW50YXRpdmVfZyArIGNhbGN1bGF0ZV9oZXVyaXN0aWModiwgY29vcmRpbmF0ZXMsIGV4aXRzKTsKICAgICAgICAgICAgICAgIG9wZW5fc2V0LnB1c2goe3YsIGYsIHRlbnRhdGl2ZV9nfSk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CiAgICByZXR1cm4ge307IAp9CgppbnQgbWFpbigpIHsKICAgIC8vIDEuIEluaXRpYWxpemUgQ29vcmRpbmF0ZXMKICAgIHZlY3RvcjxQb2ludD4gY29vcmRzX3RlbXAoMzgpOwogICAgYXV0byBhZGROb2RlID0gWyZdKHN0cmluZyBuYW1lLCBkb3VibGUgeCwgZG91YmxlIHkpIHsKICAgICAgICBpbnQgaWQgPSBnZXRJZChuYW1lKTsKICAgICAgICBjb29yZHNfdGVtcFtpZF0gPSB7eCwgeX07CiAgICB9OwoKICAgIGFkZE5vZGUoImRvb3JbMF0iLCA2MzguMCwgMjEzLjApOyBhZGROb2RlKCJkb29yWzFdIiwgNjgwLjAsIDEwNDkuMCk7CiAgICBhZGROb2RlKCJkb29yWzJdIiwgMTEwMy4wLCAxNDIwLjApOyBhZGROb2RlKCJkb29yWzNdIiwgMTY0MC4wLCAxNjI2LjApOwogICAgYWRkTm9kZSgiZG9vcls0XSIsIDE2MzUuMCwgMTE2Ny4wKTsgYWRkTm9kZSgiZG9vcls1XSIsIDIzNTguMCwgMTI5NC4wKTsKICAgIGFkZE5vZGUoImRvb3JbNl0iLCAyNzE4LjAsIDE1NzguMCk7IGFkZE5vZGUoImRvb3JbN10iLCAyNjUxLjAsIDEzNjcuMCk7CiAgICBhZGROb2RlKCJkb29yWzhdIiwgMzEzMi4wLCAxMTc5LjApOyBhZGROb2RlKCJkb29yWzldIiwgMzUyNS4wLCAxMDQ5LjApOwogICAgYWRkTm9kZSgiZG9vclsxMF0iLCAzNTIyLjAsIDE1MDUuMCk7CgogICAgYWRkTm9kZSgiZXhpdHNbMF0iLCA2MzguMCwgMTI0MC4wKTsgYWRkTm9kZSgiZXhpdHNbMV0iLCAxMDk1LjAsIDE5MTcuMCk7CiAgICBhZGROb2RlKCJleGl0c1syXSIsIDIzMTcuMCwgMTc2Ny4wKTsgYWRkTm9kZSgiZXhpdHNbM10iLCAzMDM4LjAsIDk1OS4wKTsKCiAgICBhZGROb2RlKCJjb3JpZG9yWzBdIiwgNzM2LjAsIDEzOC4wKTsgYWRkTm9kZSgiY29yaWRvclsxXSIsIDEyMjAuMCwgODE4LjApOwogICAgYWRkTm9kZSgiY29yaWRvclsyXSIsIDcxOC4wLCAxMTgyLjApOyBhZGROb2RlKCJjb3JpZG9yWzNdIiwgODg0LjAsIDE0MTcuMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzRdIiwgMTAzMi4wLCAxMzA5LjApOyBhZGROb2RlKCJjb3JpZG9yWzVdIiwgMTM4NS4wLCAxMDQ5LjApOwogICAgYWRkTm9kZSgiY29yaWRvcls2XSIsIDExODYuMCwgMTg0NS4wKTsgYWRkTm9kZSgiY29yaWRvcls3XSIsIDE0NzMuMCwgMTE2OS4wKTsKICAgIGFkZE5vZGUoImNvcmlkb3JbOF0iLCAxNDcwLjAsIDEwNDUuMCk7IGFkZE5vZGUoImNvcmlkb3JbOV0iLCAxNzQyLjAsIDE1NDUuMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzEwXSIsIDE5MzQuMCwgMTY2Ny4wKTsgYWRkTm9kZSgiY29yaWRvclsxMV0iLCAyMzIwLjAsIDE2NjUuMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzEyXSIsIDI0MzMuMCwgMTY2NS4wKTsgYWRkTm9kZSgiY29yaWRvclsxM10iLCAyNDM0LjAsIDEwNDYuMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzE0XSIsIDI0MzQuMCwgMTI5My4wKTsgYWRkTm9kZSgiY29yaWRvclsxNV0iLCAyNTU5LjAsIDEwNDkuMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzE2XSIsIDI1NTguMCwgMTM2OC4wKTsgYWRkTm9kZSgiY29yaWRvclsxN10iLCAyNTU5LjAsIDE2NjguMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzE4XSIsIDI3MjEuMCwgMTY2NS4wKTsgYWRkTm9kZSgiY29yaWRvclsxOV0iLCAzNTIzLjAsIDE2NjkuMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzIwXSIsIDMwMzguMCwgMTA0OS4wKTsgYWRkTm9kZSgiY29yaWRvclsyMV0iLCAzMTMzLjAsIDEwNDguMCk7CiAgICBhZGROb2RlKCJjb3JpZG9yWzIyXSIsIDMzOTguMCwgMTA0OC4wKTsKCiAgICAvLyAyLiBCdWlsZCBHcmFwaAogICAgdmVjdG9yPHZlY3RvcjxwYWlyPGludCwgZG91YmxlPj4+IGdyYXBoKDM4KTsKICAgIGF1dG8gYWRkRWRnZSA9IFsmXShzdHJpbmcgdSwgc3RyaW5nIHYsIGRvdWJsZSB3KSB7CiAgICAgICAgZ3JhcGhbZ2V0SWQodSldLnB1c2hfYmFjayh7Z2V0SWQodiksIHd9KTsKICAgICAgICBncmFwaFtnZXRJZCh2KV0ucHVzaF9iYWNrKHtnZXRJZCh1KSwgd30pOyAKICAgIH07CgogICAgYWRkRWRnZSgiZG9vclswXSIsICJjb3JpZG9yWzBdIiwgOS4xKTsgYWRkRWRnZSgiZG9vclswXSIsICJkb29yWzFdIiwgNjEuMDIpOwogICAgYWRkRWRnZSgiZG9vclsxXSIsICJjb3JpZG9yWzJdIiwgOS45NSk7IGFkZEVkZ2UoImRvb3JbMl0iLCAiY29yaWRvcls0XSIsIDkuMzMpOwogICAgYWRkRWRnZSgiZG9vclsyXSIsICJkb29yWzNdIiwgNDEuNTMpOyBhZGRFZGdlKCJkb29yWzNdIiwgImNvcmlkb3JbOV0iLCA5LjUyKTsKICAgIGFkZEVkZ2UoImRvb3JbNF0iLCAiY29yaWRvcls3XSIsIDEyLjAyKTsgYWRkRWRnZSgiZG9vcls0XSIsICJkb29yWzVdIiwgNTIuODApOwogICAgYWRkRWRnZSgiZG9vcls1XSIsICJjb3JpZG9yWzE0XSIsIDUuNzQpOyBhZGRFZGdlKCJkb29yWzZdIiwgImRvb3JbN10iLCAxNS43Nyk7CiAgICBhZGRFZGdlKCJkb29yWzZdIiwgImNvcmlkb3JbMThdIiwgNi40OCk7IGFkZEVkZ2UoImRvb3JbN10iLCAiZG9vcls4XSIsIDM3LjkyKTsKICAgIGFkZEVkZ2UoImRvb3JbN10iLCAiY29yaWRvclsxNl0iLCA2Ljg3KTsgYWRkRWRnZSgiZG9vcls4XSIsICJjb3JpZG9yWzIxXSIsIDkuMjcpOwogICAgYWRkRWRnZSgiZG9vcls5XSIsICJkb29yWzEwXSIsIDMzLjA5KTsgYWRkRWRnZSgiZG9vcls5XSIsICJjb3JpZG9yWzIyXSIsIDkuMTMpOwogICAgYWRkRWRnZSgiZG9vclsxMF0iLCAiY29yaWRvclsxOV0iLCAxMi4wNik7CiAgICBhZGRFZGdlKCJleGl0c1swXSIsICJjb3JpZG9yWzJdIiwgNi44NCk7IGFkZEVkZ2UoImV4aXRzWzFdIiwgImNvcmlkb3JbNl0iLCA4LjM5KTsKICAgIGFkZEVkZ2UoImV4aXRzWzJdIiwgImNvcmlkb3JbMTFdIiwgNi41Myk7IGFkZEVkZ2UoImV4aXRzWzNdIiwgImNvcmlkb3JbMjBdIiwgNi40OSk7CiAgICBhZGRFZGdlKCJjb3JpZG9yWzBdIiwgImNvcmlkb3JbMV0iLCA2MC44MSk7IGFkZEVkZ2UoImNvcmlkb3JbMV0iLCAiY29yaWRvclsyXSIsIDQ1LjI5KTsKICAgIGFkZEVkZ2UoImNvcmlkb3JbMV0iLCAiY29yaWRvcls1XSIsIDIwLjMxKTsgYWRkRWRnZSgiY29yaWRvclsyXSIsICJjb3JpZG9yWzNdIiwgMjAuOTgpOwogICAgYWRkRWRnZSgiY29yaWRvclszXSIsICJjb3JpZG9yWzRdIiwgMTMuMzYpOyBhZGRFZGdlKCJjb3JpZG9yWzNdIiwgImNvcmlkb3JbNl0iLCAzOC40MSk7CiAgICBhZGRFZGdlKCJjb3JpZG9yWzRdIiwgImNvcmlkb3JbNV0iLCAzMS41MSk7IGFkZEVkZ2UoImNvcmlkb3JbNV0iLCAiY29yaWRvcls3XSIsIDEwLjc1KTsKICAgIGFkZEVkZ2UoImNvcmlkb3JbNV0iLCAiY29yaWRvcls4XSIsIDYuMDgpOyBhZGRFZGdlKCJjb3JpZG9yWzddIiwgImNvcmlkb3JbOV0iLCAzNC4yMik7CiAgICBhZGRFZGdlKCJjb3JpZG9yWzhdIiwgImNvcmlkb3JbMTNdIiwgNzAuNDUpOyBhZGRFZGdlKCJjb3JpZG9yWzldIiwgImNvcmlkb3JbMTBdIiwgMTYuMjkpOwogICAgYWRkRWRnZSgiY29yaWRvclsxMF0iLCAiY29yaWRvclsxMV0iLCAyOC4yNCk7IGFkZEVkZ2UoImNvcmlkb3JbMTFdIiwgImNvcmlkb3JbMTJdIiwgOC41Mik7CiAgICBhZGRFZGdlKCJjb3JpZG9yWzEyXSIsICJjb3JpZG9yWzE0XSIsIDI2LjY0KTsgYWRkRWRnZSgiY29yaWRvclsxMl0iLCAiY29yaWRvclsxN10iLCA4LjkyKTsKICAgIGFkZEVkZ2UoImNvcmlkb3JbMTNdIiwgImNvcmlkb3JbMTRdIiwgMTcuNzYpOyBhZGRFZGdlKCJjb3JpZG9yWzEzXSIsICJjb3JpZG9yWzE1XSIsIDguODgpOwogICAgYWRkRWRnZSgiY29yaWRvclsxNV0iLCAiY29yaWRvclsxNl0iLCAyMy4yMSk7IGFkZEVkZ2UoImNvcmlkb3JbMTVdIiwgImNvcmlkb3JbMjBdIiwgMzQuNzQpOwogICAgYWRkRWRnZSgiY29yaWRvclsxNl0iLCAiY29yaWRvclsxN10iLCAyMS4yNCk7IGFkZEVkZ2UoImNvcmlkb3JbMTddIiwgImNvcmlkb3JbMThdIiwgMTEuNjQpOwogICAgYWRkRWRnZSgiY29yaWRvclsxOF0iLCAiY29yaWRvclsxOV0iLCA1OS4wOCk7IGFkZEVkZ2UoImNvcmlkb3JbMjBdIiwgImNvcmlkb3JbMjFdIiwgNy4wMik7CiAgICBhZGRFZGdlKCJjb3JpZG9yWzIxXSIsICJjb3JpZG9yWzIyXSIsIDE5LjQ4KTsKCiAgICB1bm9yZGVyZWRfc2V0PGludD4gZXhpdF9pZHMgPSB7CiAgICAgICAgZ2V0SWQoImV4aXRzWzBdIiksIGdldElkKCJleGl0c1sxXSIpLCAKICAgICAgICBnZXRJZCgiZXhpdHNbMl0iKSwgZ2V0SWQoImV4aXRzWzNdIikKICAgIH07CgogICAgdmVjdG9yPHN0cmluZz4gYWxsX2Rvb3JzID0gewogICAgICAgICJkb29yWzBdIiwgImRvb3JbMV0iLCAiZG9vclsyXSIsICJkb29yWzNdIiwgImRvb3JbNF0iLAogICAgICAgICJkb29yWzVdIiwgImRvb3JbNl0iLCAiZG9vcls3XSIsICJkb29yWzhdIiwgImRvb3JbOV0iLCAiZG9vclsxMF0iCiAgICB9OwoKICAgIGNvdXQgPDwgIi0tLSBFVkFDVUFUSU9OIFNZU1RFTSBDT05UUk9MIC0tLVxuIjsKICAgIGNvdXQgPDwgIkVudGVyIHRoZSBudW1iZXIgb2YgYWN0aXZlIGZpcmUgem9uZXM6ICI7CiAgICBpbnQgbnVtX2ZpcmVzOwogICAgY2luID4+IG51bV9maXJlczsKCiAgICB1bm9yZGVyZWRfc2V0PGludD4gYWN0aXZlX2ZpcmVzOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBudW1fZmlyZXM7ICsraSkgewogICAgICAgIHN0cmluZyBmaXJlX2xvY2F0aW9uOwogICAgICAgIGNvdXQgPDwgIkVudGVyIGxvY2F0aW9uIGZvciBGaXJlICIgPDwgKGkgKyAxKSA8PCAiIChlLmcuLCBjb3JpZG9yWzE1XSwgZG9vclsxXSk6ICI7CiAgICAgICAgY2luID4+IGZpcmVfbG9jYXRpb247CgogICAgICAgIGlmIChub2RlX2lkcy5maW5kKGZpcmVfbG9jYXRpb24pICE9IG5vZGVfaWRzLmVuZCgpKSB7CiAgICAgICAgICAgIGFjdGl2ZV9maXJlcy5pbnNlcnQoZ2V0SWQoZmlyZV9sb2NhdGlvbikpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGNvdXQgPDwgIldhcm5pbmc6IE5vZGUgJyIgPDwgZmlyZV9sb2NhdGlvbiA8PCAiJyBkb2VzIG5vdCBleGlzdC4gU2tpcHBpbmcuXG4iOwogICAgICAgIH0KICAgIH0KCiAgICBjb3V0IDw8ICJcbkNhbGN1bGF0aW5nIHNhZmVzdCByb3V0ZXMgYXZvaWRpbmcgYWxsIGZpcmUgem9uZXMuLi5cbiI7CiAgICBjb3V0IDw8IHN0cmluZyg2MCwgJz0nKSA8PCAiXG5cbiI7CgogICAgZm9yIChjb25zdCBzdHJpbmcmIGRvb3JfbmFtZSA6IGFsbF9kb29ycykgewogICAgICAgIGNvdXQgPDwgIkZyb20gIiA8PCBkb29yX25hbWUgPDwgIjpcbiI7CgogICAgICAgIGlmIChhY3RpdmVfZmlyZXMuY291bnQoZ2V0SWQoZG9vcl9uYW1lKSkpIHsKICAgICAgICAgICAgY291dCA8PCAiICBbIV0gVEhJUyBET09SIElTIE9OIEZJUkUuIEVWQUNVQVRJT04gSU1QT1NTSUJMRS5cblxuIjsKICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgfQoKICAgICAgICBkb3VibGUgdG90YWxfZGlzdGFuY2UgPSAwLjA7CiAgICAgICAgdmVjdG9yPGludD4gcGF0aCA9IGFfc3Rhcl9ldmFjdWF0aW9uKGdldElkKGRvb3JfbmFtZSksIGV4aXRfaWRzLCBncmFwaCwgY29vcmRzX3RlbXAsIGFjdGl2ZV9maXJlcywgdG90YWxfZGlzdGFuY2UpOwoKICAgICAgICBpZiAoIXBhdGguZW1wdHkoKSkgewogICAgICAgICAgICBjb3V0IDw8ICIgIFBhdGg6ICI7CiAgICAgICAgICAgIGZvciAoaW50IGkgPSAwOyBpIDwgcGF0aC5zaXplKCk7ICsraSkgewogICAgICAgICAgICAgICAgY291dCA8PCBub2RlX25hbWVzW3BhdGhbaV1dIDw8IChpID09IHBhdGguc2l6ZSgpIC0gMSA/ICIiIDogIiAtPiAiKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgZG91YmxlIGNsZWFyX3RpbWVfc2VjID0gdG90YWxfZGlzdGFuY2UgLyA0LjA7CiAgICAgICAgICAgIGRvdWJsZSBzbW9rZV90aW1lX3NlYyA9IHRvdGFsX2Rpc3RhbmNlIC8gMS41OwoKICAgICAgICAgICAgY291dCA8PCBmaXhlZCA8PCBzZXRwcmVjaXNpb24oMik7CiAgICAgICAgICAgIGNvdXQgPDwgIlxuICBEaXN0YW5jZTogIiA8PCB0b3RhbF9kaXN0YW5jZSA8PCAiIGZ0IjsKICAgICAgICAgICAgY291dCA8PCAiXG4gIEVzdC4gVGltZSAoQ2xlYXIpOiAiIDw8IGNsZWFyX3RpbWVfc2VjIDw8ICIgc2Vjb25kcyI7CiAgICAgICAgICAgIGNvdXQgPDwgIlxuICBFc3QuIFRpbWUgKFNtb2t5KTogIiA8PCBzbW9rZV90aW1lX3NlYyA8PCAiIHNlY29uZHNcblxuIjsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBjb3V0IDw8ICIgIFshXSBOTyBTQUZFIFJPVVRFLiBGSVJFUyBCTE9DSyBBTEwgUEFUSFMgVE8gRVhJVFMuXG5cbiI7CiAgICAgICAgfQogICAgfQoKICAgIHJldHVybiAwOwp9